1. 程式人生 > >codeforces111D. Petya and Coloring(組合數學,計數問題)

codeforces111D. Petya and Coloring(組合數學,計數問題)

targe n) 假設 its 說明 ans 直線 str target

傳送門:

解題思路:

要求一條直線分割矩陣時左右顏色數一樣,那麽就說明一個問題。
直線左右移動時是不會改變左右矩陣的顏色集合的。
所以說明:2~m-1列的顏色集一定屬於第一列與第m列顏色集的交集。
而且第一列與第m列顏色集大小相等。
顯然需要預處理n個點m種顏色的方案數,設為$g(i,j)$
這樣,只需要確定第一列和最後一列顏色集,假設交集是$i$種顏色,
就可以算出中間的顏色方案數:$i^{n*(m-2)}$
假設兩邊顏色個數都是$j$($j\ge i$)那麽兩邊顏色的答案($(g(n,j)j!)^2$)
這$i$種顏色共有$C_k^i$種選法,兩邊各$j$種顏色,且只有$i$種顏色相同的方案就是:
$\Large C_k^iC_{k-i}^{2(j-i)}C_{2(j-i)}^{j-i}$


那麽答案就是
$\Large\sum\limits_{i=1}^{n}\sum\limits_{j=i}^{n}C_k^iC_{k-i}^{2(j-i)}C_{2(j-i)}^{j-i}{(g(n,j)j!)^2}{i^{n(m-2)}}$
代碼:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 typedef long long lnt;
 5 const lnt mod=1000000007;
 6 lnt g[1010][1010];
 7 lnt fac[1000010];
 8 lnt inv[1000010
]; 9 int n,m,k; 10 lnt ans; 11 lnt ksm(lnt a,lnt b) 12 { 13 lnt ans=1; 14 while(b) 15 { 16 if(b&1)ans=ans*a%mod; 17 a=a*a%mod; 18 b=b/2; 19 } 20 return ans; 21 } 22 lnt C(int x,int y) 23 { 24 if(y>x)return 0; 25 return fac[x]*inv[y]%mod*inv[x-y]%mod;
26 } 27 lnt squ(lnt x) 28 { 29 return x*x%mod; 30 } 31 int main() 32 { 33 g[0][0]=1; 34 fac[0]=inv[0]=fac[1]=inv[1]=1; 35 for(int i=2;i<=1000000;i++) 36 { 37 fac[i]=(fac[i-1]*i)%mod; 38 inv[i]=(mod-mod/i)*inv[mod%i]%mod; 39 } 40 for(int i=1;i<=1000000;i++)inv[i]=inv[i]*inv[i-1]%mod; 41 scanf("%d%d%d",&n,&m,&k); 42 if(m==1) 43 { 44 printf("%I64d\n",ksm(k,n)); 45 return 0; 46 } 47 for(int i=1;i<=n;i++) 48 { 49 for(int j=1;j<=i&&j<=k;j++) 50 { 51 g[i][j]=(g[i-1][j-1]+g[i-1][j]*j)%mod; 52 } 53 } 54 for(int i=0;i<=n;i++) 55 { 56 lnt tmp=ksm(i,n*(m-2))*C(k,i)%mod; 57 for(int j=i;j<=n;j++) 58 { 59 ans=(ans+tmp*C(k-i,(j-i)*2)%mod*C((j-i)*2,j-i)%mod*squ(g[n][j]*fac[j]%mod)%mod)%mod; 60 } 61 } 62 printf("%I64d\n",(ans%mod+mod)%mod); 63 return 0; 64 }

codeforces111D. Petya and Coloring(組合數學,計數問題)