http://codeforces.com/contest/111/problem/D
Little Petya loves counting. He wants to count the number of ways to paint a rectangular checkered board of size n × m (n rows, m columns) in k colors. Besides, the coloring should have the following property: for any vertical line that passes along the grid lines and divides the board in two non-empty parts the number of distinct colors in both these parts should be the same. Help Petya to count these colorings.
The first line contains space-separated integers n, m and k (1 ≤ n, m ≤ 1000, 1 ≤ k ≤ 106) — the board's vertical and horizontal sizes and the number of colors respectively.
Print the answer to the problem. As the answer can be quite a large number, you should print it modulo 109 + 7 (1000000007).
2 2 1
1
2 2 2
8
3 2 2
40
题意:
给出一个 n*m 的矩阵,用sum种颜色染色
满足:任意一条竖直线(纵线)把矩阵划分成的2个部分,2个部分的不同的颜色数相同
求方案数
n,m <= 10^3,sum <= 10^6
solution:
注意到n,m的范围不大
显然有以下性质:
1.第1列和第m列的颜色数一定相等
2.2~m-1列的颜色只能从1,m列的颜色的交集中选择
m=1的时候,特殊处理,ans=k^n
m>1的时候,我们只需要考虑1,m列的颜色选择还有交集大小
预处理g[i]表示恰好用i种颜色涂满n个格子的方案数
(此时n是一个常量)
如果用递推式,求g[i]需要O(n^2),求g数组需要O(n^3),tle
考虑二项式反演求g数组:
设h(i)表示用i种颜色染n个格子的方案数,则h(i) = i^n
g(i)表示恰好用i种颜色染n个格子的方案数,
有:h(y) = sigma(i=0,i<=y)(C(y,i)*g(i))
则:g(y) = sigma(i=0,i<=y)((-1)^(y-i) * C(y,i) * h(i))
= sigma(i=0,i<=y)((-1)^(y-i) * C(y,i) * i^n)
这样求g[i]需要O(nlogn),求g数组需要O(n^2*logn)
当然也可以优化到O(n^2)求g数组
主要的预处理部分搞定了,然后就是答案了
退下公式,得到:
ans = sigma(j=0,j<=min(n,sum))C(sum,j) * j^(m*n-2*n) *
(sigma(i=0,i<=min(n-j,(sum-j)/2))(C(sum-j,i)*C(sum-j-i,i)*g(i+j)^2))
1 2 //File Name: cf111D.cpp 3 //Author: long 4 //Mail: 736726758@qq.com 5 //Created Time: 2016年05月16日 星期一 01时13分24秒 6 7 8 #include <stdio.h> 9 #include <algorithm> 10 #include <iostream> 11 #include <string.h> 12 #include <stdlib.h> 13 #include <math.h> 14 15 #define LL long long 16 17 using namespace std; 18 19 const int MAXN = 1000 + 3; 20 const int MAXM = 1000000 + 3; 21 const int MOD = (int)1e9 + 7; 22 23 LL jie[MAXM]; 24 LL g[MAXN]; 25 26 LL qp(LL x,LL y){ 27 LL res = 1; 28 while(y){ 29 if(y & 1) res = res * x % MOD; 30 x = x * x % MOD; 31 y >>= 1; 32 } 33 return res; 34 } 35 36 LL get_c(LL x,LL y){ 37 if(x < y) return 0; 38 if(x == y || y == 0) return 1; 39 return jie[x] * qp(jie[y] * jie[x - y] % MOD,MOD - 2) % MOD; 40 } 41 42 void init(int sum,int n){ 43 jie[0] = 1; 44 for(int i=1;i<MAXM;i++) 45 jie[i] = jie[i-1] * i % MOD; 46 int ma = min(sum,n); 47 LL now; 48 for(int i=0;i<=ma;i++){ 49 for(int k=0;k<=i;k++){ 50 now = get_c(i,k) * qp(k,n) % MOD; 51 if((i - k) % 2) 52 g[i] = (g[i] - now + MOD) % MOD; 53 else 54 g[i] = (g[i] + now) % MOD; 55 } 56 } 57 } 58 59 LL solve(int n,int m,int sum){ 60 if(m == 1) return qp(sum,n); 61 init(sum,n); 62 LL ans = 0,now,tmp; 63 int ma = min(sum,n); 64 for(int j=0,ma2;j<=ma;j++){ 65 now = 0; 66 ma2 = min(n - j,(sum - j) / 2); 67 for(int i=0;i<=ma2;i++){ 68 (now += jie[sum-j] * qp(jie[sum-j-2*i]*jie[i]%MOD*jie[i]%MOD,MOD - 2) % MOD 69 * g[i+j] % MOD * g[i+j] % MOD) %= MOD; 70 } 71 tmp = qp(j,(m - 2) * n) % MOD; 72 (ans += get_c(sum,j) * tmp % MOD * now % MOD) %= MOD; 73 //cout << j << " " <<ans << endl; 74 } 75 return ans; 76 } 77 78 int main(){ 79 int n,m,k; 80 scanf("%d %d %d",&n,&m,&k); 81 printf("%d ",(int)solve(n,m,k)); 82 return 0; 83 }