很明显这是一道组合题。
首先特判一下,当 \(m=1\) 时,答案就是 \(k^n\)。
对于 \(m>1\) 的情况,我们可以得出一个结论:
对于沿格子的线穿过的任何垂直线,会将棋盘分成两个非空的部分,这两个部分中的不同颜色的数量相同且总是不变。设这个不同颜色的数量为 \(i\),那么左边这部分的颜色一定是固定的 \(i\) 种颜色,右边那部分也一定是固定的 \(i\) 种颜色。
由于分开前两列的垂线的左边部分只有第一列的格子,所以左边的 \(i\) 种颜色中的每一种都一定至少会在第一列中出现一次,最后一列同理。
我们设左边的颜色为集合 \(S1\),右边的颜色为集合 \(S2\),\(|S1|=|S2|=i\),那么中间的 \(m-2\) 列能染的颜色的种数为 \(|S1\cap S2|\)。
我们可以先枚举 \(i\),再枚举 \(|S1\cap S2|\) (设其为 \(j\)),那么 \(S1\) 和 \(S2\) 就总共有 \({k \choose j}{k-j \choose i-j}{k-i \choose i-j}\) 种取法,给中间 \(m-2\) 列染色的方案数为 \(j^{(m-2)n}\),给前后两列染色的方案数可以用容斥得出。
我们设 \(f(m)\) 表示给 \(n\) 个格子染 \(m\) 种颜色每一种颜色都至少出现一次的方案数,我们用容斥得出 \(f(m)\):
\[f(m)=\sum_{i=0}^{m} (-1)^i\times {m\choose i}(m-i)^n \]整理得到最终的答案:
\[\sum_{i=1}^{min(k,n)} f(i)^2\times \sum_{j=0}^{i} {k \choose j}{k-j \choose i-j}{k-i \choose i-j}\times j^{(m-2)n} \]这个值可以在 \(O(n^2(\log n+\log m))\) 的时间复杂度内得到。
代码:
#include<bits/stdc++.h>
#define ll long long
#define mxn 1000003
#define md 1000000007
#define rep(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
int n,m,k,xi,inv[mxn],fac[mxn],ifac[mxn];
ll ans;
inline ll C(int n,int m){
if(m>n)return 0;
return (ll)fac[n]*ifac[n-m]%md*ifac[m]%md;
}
inline ll power(ll x,int y){
ll ans=1;
for(;y;y>>=1){
if(y&1)ans=ans*x%md;
x=x*x%md;
}
return ans;
}
inline ll p2(ll x){
return x*x%md;
}
ll color(int n,int m){
ll ans=0;
xi=1;
rep(i,0,m){
ans=(ans+C(m,i)*power(m-i,n)%md*xi)%md;
xi*=-1;
}
return ans;
}
signed main(){
cin>>n>>m>>k;
if(m==1){
cout<<power(k,n);
return 0;
}
inv[1]=fac[1]=ifac[1]=1;
fac[0]=ifac[0]=1;
rep(i,2,1000000){
inv[i]=(ll)inv[md%i]*(md-md/i)%md;
fac[i]=(ll)fac[i-1]*i%md;
ifac[i]=(ll)ifac[i-1]*inv[i]%md;
}
rep(i,1,min(n,k)){
xi=p2(color(n,i));
rep(j,0,i)ans=(ans+C(k,j)*C(k-j,i-j)%md*C(k-i,i-j)%md*xi%md*power(j,(m-2)*n))%md;
}
cout<<(ans+md)%md;
return 0;
}
标签:md,Coloring,return,题解,ll,choose,ans,CF111D,S1
From: https://www.cnblogs.com/zifanoi/p/18039982