codeforces111D. Petya and Coloring(组合数学,计数问题)

传送门:

解题思路:

要求一条直线分割矩阵时左右颜色数一样,那么就说明一个问题。
直线左右移动时是不会改变左右矩阵的颜色集合的。
所以说明: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 }
posted @ 2019-02-16 22:03  Unstoppable728  阅读(279)  评论(0编辑  收藏  举报