【计数】CF111D. Petya and Coloring
http://codeforces.com/problemset/problem/111/D
中等偏难的计数题目,题意很奇葩,用K种颜色染一个n*m的矩形使得从竖直方向任意位置切成两个矩阵时两个子矩阵包含颜色种类数目一样多,当然要沿着网格线切。非常奇葩的题意,后来仔细一想发现这个要求可以证明等同于这个条件,最左最右列包含颜色种类数目一样多同时中间列出现的颜色只能是最左最右同时出线过的颜色。证明略,知道结论后反证异常简单,推出结论比较难。
综上,做法很easy,枚举左右都出现的颜色数目i,已经单独出现的颜色(对方没用的)颜色数j , ans = sum(dp[i+j]*dp[i+j]*C(k,i)*C(k-i,j)*C(k-i-j,j)*i^(n*(m-2)))....dp[i]表示n个格子使用i种颜色染的方案数。
最后,附程序 Ps,C操作可以通过预处理达到O(1)复杂度, m=1,m=2要特殊处理一下
View Code
1 //By Lin 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define maxn 1005 6 #define maxK 1000050 7 #define MOD 1000000007 8 using namespace std; 9 typedef long long LL; 10 11 int n,m,K; 12 LL dp[maxn]; 13 14 LL quick_sqr(LL g , int K ){ 15 LL ret = 1; 16 while ( K ) { 17 if ( K&1 ) ret = ret*g%MOD; 18 g = g*g%MOD; 19 K>>= 1; 20 } 21 return ret; 22 } 23 24 LL sum[maxK],psum[maxK]; 25 inline LL C(int a ,int b) { 26 return sum[a]*psum[b]%MOD*psum[a-b]%MOD; 27 } 28 int main(){ 29 sum[0] = psum[0] = 1; 30 for (int i = 1; i<maxK; i++) { 31 sum[i] = sum[i-1]*i%MOD; 32 psum[i] = quick_sqr( sum[i],MOD-2); 33 } 34 scanf("%d%d%d", &n,&m, &K ); 35 for (int i = 1; i<=n; i++) { 36 dp[i] = quick_sqr(i,n); 37 for (int k = 1; k<i; k++) { 38 dp[i] += MOD-C(i,k)*dp[k]%MOD; 39 dp[i] %= MOD; 40 } 41 } 42 LL A = 1, ans = 0; 43 if ( m == 1 ){ 44 printf("%I64d\n", quick_sqr(K,n) ); 45 return 0; 46 } 47 if ( m == 2 ){ 48 for (int j = 1,i = 0; i+j*2<=K && i+j<=n; j++){ 49 ans += dp[i+j]*dp[i+j]%MOD*C(K,i)%MOD*C(K-i,j)%MOD*C(K-i-j,j)%MOD*quick_sqr(i,n*(m-2))%MOD; 50 if ( ans >= MOD ) ans -= MOD; 51 } 52 } 53 for (int i = 1; i<=min(n,K); i++ ) 54 for (int j = 0; i+j*2<=K && i+j<=n ; j++){ 55 ans += dp[i+j]*dp[i+j]%MOD*C(K,i)%MOD*C(K-i,j)%MOD*C(K-i-j,j)%MOD*quick_sqr(i,n*(m-2))%MOD; 56 if ( ans >= MOD ) ans -= MOD; 57 } 58 printf("%I64d\n" , ans ); 59 60 }