题意: T T T组数据,每组数据给你三个正整数 n , m , k n,m,k n,m,k,其中 n , m n,m n,m分别为积木的长和宽(积木由若干个 1 × 1 × 1 1\times1\times1 1×1×1的小方块组成),再给你左前视图(如图所示)每一部分的最终高度 a i a_i ai(共 n + m n+m n+m部分),接下来 k k k行,每行三个正整数 x , y , h x,y,h x,y,h,表示第 x x x行 y y y列的高度指定为正整数 h h h,问你合法的积木总数,对 1 0 9 + 7 10^9+7 109+7 取模。
我们设 c n t i cnt_i cnti 为 i i i 与 i + 1 i+1 i+1间夹的部分,高度随意的木块儿的数量, p i p_i pi 为固定的第 i i i 部分木块儿的最大高度。
函数 c a l ( h , c n t ) cal(h,cnt) cal(h,cnt) 表示搭 cnt 个木块儿,最高高度为 h,且至少有一个达到 h 的方案数。 p o w e r ( h , c n t ) power(h, cnt) power(h,cnt) 表示搭 cnt 个木块儿,最高高度为 h,可以没有木块儿达到 h。
f ( i , 0 / 1 ) f(i, 0/1) f(i,0/1) 表示第 i 个部分,0 没有到达最大高度,1 达到最大高度。而这个部分的与 a i a_i ai 和 a i − 1 a_{i-1} ai−1 的限制有关。
有一个地方容易想错,就是说,n个位置,至少有一个高度为h(每个位置最小高度为1),求方案数。可以这样考虑,选择 k k k 个位置高度到 h h h,那么 n − k n-k n−k 个位置最多到 h − 1 h-1 h−1,那么方案数为 ∑ k = 1 n C n k ( h − 1 ) n − k = ∑ k = n C n k ( h − 1 ) n − k − ( h − 1 ) n = k n − ( k − 1 ) n \sum\limits_{k=1}^nC_{n}^{k}(h-1)^{n-k} = \sum\limits_{k=}^nC_{n}^{k}(h-1)^{n-k}-(h-1)^n = k^n-(k-1)^n k=1∑nCnk(h−1)n−k=k=∑nCnk(h−1)n−k−(h−1)n=kn−(k−1)n. 这样子的话,样例1就讲通了。 题解
#include<iostream>#include<cstring>#include<algorithm>usingnamespace std;constint maxn =200010;typedeflonglong ll;const ll mod =1e9+7;int kase, len, N, M, K;
ll f[maxn][2], p[maxn], a[maxn], cnt[maxn];
ll power(ll h, ll cnt){
ll res =1;while(cnt){
if(cnt &1) res = res * h % mod;
h = h * h % mod;
cnt >>=1;}return res;}
ll cal(ll h, ll cnt){
return(power(h, cnt)-power(h -1, cnt)+ mod)% mod;}
ll solve(){
scanf("%d%d%d",&N,&M,&K);
len = N + M;for(int i =0; i <= len; i++) f[i][0]= f[i][1]= p[i]= a[i]= cnt[i]=0;for(int i =1; i <= len; i++){
scanf("%lld",&a[i]);
cnt[i +1]=min(min(N, M),min(i, N + M - i));}for(int i =1; i <= K; i++){
int x, y, h;scanf("%d%d%d",&x,&y,&h);
p[x + y]=max(p[x + y],(ll)h);
p[x + y -1]=max(p[x + y -1],(ll)h);
cnt[x + y]--;}//for (int i = 1; i <= len; i++) printf("%lld ", cnt[i]);
f[1][0]= f[1][1]=1;if(a[1]== p[1]) f[1][0]=0;else f[1][1]=0;for(int i =2; i <= len; i++){
//这个是循环每一斜对角线的方块儿if(p[i]> a[i])return0;if(p[i]< a[i]){
if(a[i -1]>= a[i]){
f[i][1]=(f[i][1]+ f[i -1][1]*cal(a[i], cnt[i])% mod)% mod;}if(a[i -1]== a[i]){
f[i][1]=(f[i][1]+ f[i -1][0]*cal(a[i], cnt[i])% mod)% mod;}
f[i][0]=(f[i][0]+ f[i -1][1]*power(min(a[i -1], a[i]-1), cnt[i])% mod)% mod;if(a[i -1]< a[i]){
f[i][0]=(f[i][0]+ f[i -1][0]*cal(a[i -1], cnt[i])% mod)% mod;}}if(p[i]== a[i]){
f[i][1]=(f[i][1]+ f[i -1][1]*power(min(a[i -1], a[i]), cnt[i])% mod)% mod;if(a[i -1]<= a[i]){
f[i][1]=(f[i][1]+ f[i -1][0]*cal(a[i -1], cnt[i])% mod)% mod;}
f[i][0]=0;}}return f[len][1];}intmain(){
int T;scanf("%d",&T);while(T--){
printf("Case #%d: %lld\n",++kase,solve());}return0;}
B. Code a Trie
Trie+LCA
C. Escape from the Island
图论+dp
D. Fracture Ray
树套树
E. Game of Cards
博弈:你有 c 0 c_0 c0 张值为0的卡, c 1 c_1 c1 张值为1的卡, c 2 c_2 c2 张值为2的卡, c 3 c_3 c3 张值为3的卡,每次操作你可以选取俩张和小于等于3的卡,让他们融合为一张卡,融合的卡的值为他们俩张卡的和,rabbit先手,不能操作的人输。
三种必输态:
c 0 = 0 , c 1 = 0 c_0 = 0, c_1 = 0 c0=0,c1=0.
c 0 = 1 , c 2 , c 3 , c 4 = 0 c_0=1, c_2, c_3, c_4 = 0 c0=1,c2,c3,c4=0.
c 0 = 0 , c 1 = 1 , c 2 = 0 c_0 = 0, c_1 = 1, c_2 = 0 c0=0,c1=1,c2=0.
有三种变化
c 0 , c 1 − 1 , c 2 , c 3 c_0, c_1-1, c_2, c_3 c0,c1−1,c2,c3
c 0 , c 1 − 1 , c 2 − 1 , c 3 + 1 c_0, c_1 - 1, c_2 - 1, c_3 + 1 c0,c1−1,c2−1,c3+1.
c 0 , c 1 − 2 , c 2 + 1 , c 3 c_0, c_1 - 2, c_2 + 1, c_3 c0,c1−2,c2+1,c3
接着我们发现,只要 c 2 ≥ 1 c_2 \ge 1 c2≥1,无论先手如何,后手都可以让 c 0 c_0 c0 减 2 或者 c 1 c_1 c1 减3. 因此, c 2 = 0 c_2 = 0 c2=0 需要特判,其他的直接深搜就行。