HDU4290 Counting Formations
Staginner剽悍地抽象出了数学模型并推出了各项数据,万事俱备只欠东风,可惜最后一步推错了,比赛时候这题没过。。
插板法组合数对应了不同icon的分配方案。
每种分配方案又对应了很多种摆放方案。
假设有s种分配方案,x[1]~x[s]分别对应了每种分配方案的摆放方案,可通过DP求得∑x 。
也可求得∑(x^2),∑(x^3)...
设M[p] = ∑(x^p)。
设S[p] = ∑x[i1]*x[i2]*x[i3]...*x[ip],i1~ip为互不相同的1~s的序列。
题目所给棋盘是N行,我们求的就是S[N] * N!
——————————分割线——————————
以上是Staginner得到的数据与结论,详细的推导他应该会写博客的。
接下来要解决的就是如何用M[1]~M[p]来推S[p],没想到被我一推一推推出来了。
首先S[1] == M[1]
S[1] * M[1]的结果是两两不同x的积和相同x的平方项,那么去掉平方项就是S[2]的若干倍。
在S[1] * M[1]的过程中每一个S[2]的项都由两个组成它的x乘得一次,所以这个“若干倍”是2。
S[2] = (S[1] * M[1] - M[2]) / 2
递推过去,最终
S[p] = (S[p - 1] * M[1] - S[p - 2] * M[2] + S[p - 3] * M[3] - S[p - 4] * M[4] ....) / p
这样就得到了S[N]。
中间该取模取模,该求逆元求逆元,就OK了。
代码是在Staginner代码上改的。
1 #include<stdio.h> 2 #include<string.h> 3 #define MAXD 33 4 #define MOD 1000000007 5 typedef __int64 LL; 6 int N, M, K; 7 LL C[MAXD][MAXD][MAXD], f[MAXD][MAXD], X[MAXD], S[MAXD]; 8 LL fac(LL a, int n) 9 { 10 int i; 11 LL ans = 1; 12 for(i = 0; i < n; i ++) ans = ans * a % MOD; 13 return ans; 14 } 15 void exgcd(LL a, LL b, LL &x, LL &y) 16 { 17 if(b == 0) x = 1, y = 0; 18 else exgcd(b, a % b, y, x), y -= x * (a / b); 19 } 20 LL getinv(LL a) 21 { 22 LL x, y; 23 exgcd(a, MOD, x, y); 24 x %= MOD; 25 if(x < 0) x += MOD; 26 return x; 27 } 28 void prepare() 29 { 30 int i, j, t; 31 memset(C, 0, sizeof(C)); 32 C[1][0][0] = 1; 33 for(i = 1; i <= 32; i ++) 34 { 35 C[1][i][0] = 1; 36 for(j = 1; j <= i; j ++) 37 C[1][i][j] = (C[1][i - 1][j] + C[1][i - 1][j - 1]) % MOD; 38 } 39 for(t = 2; t <= 32; t ++) 40 for(i = 0; i <= 32; i ++) 41 for(j = 0; j <= 32; j ++) 42 C[t][i][j] = fac(C[1][i][j], t); 43 } 44 void solve() 45 { 46 int i, j, k, t; 47 LL ans; 48 for(t = 1; t <= N; t ++) 49 { 50 memset(f, 0, sizeof(f)), f[0][0] = 1; 51 for(i = 1; i <= K; i ++) 52 for(j = 0; j <= M; j ++) 53 for(k = 0; k <= j; k ++) 54 f[i][j] = (f[i][j] + f[i - 1][k] * C[t][M - k][j - k]) % MOD; 55 X[t] = f[K][M]; 56 } 57 S[0] = 1, S[1] = X[1]; 58 for(i = 2; i <= N; i ++) 59 { 60 S[i] = 0; 61 for(j = 1; j <= i; j ++) 62 { 63 if(j & 1) S[i] = (S[i] + S[i - j] * X[j] + MOD) % MOD; 64 else S[i] = (S[i] - S[i - j] * X[j] + MOD) % MOD; 65 } 66 S[i] = S[i] * getinv(i) % MOD; 67 } 68 ans = S[N]; 69 for(i = 2; i <= N; i ++) ans = ans * i % MOD; 70 if(ans < 0) ans += MOD; 71 printf("%I64d\n", ans); 72 } 73 int main() 74 { 75 prepare(); 76 while(scanf("%d%d%d", &N, &M, &K) == 3) 77 solve(); 78 return 0; 79 }