[JSOI2015]染色问题
做题日期:2020.11.03
\(【题目描述】\)
有一个\(N \times M(N,M\leq 400)\)的矩阵,有\(C(C \leq 400)\)种颜色,现在要把这\(C\)种颜色全部涂在矩阵的格子中,要求每一行、每一列都至少得有一格被染色,每一种颜色都至少得涂一格。问有多少种涂色方案。
\(【输入样例】\)
2 2 3
\(【输出样例】\)
60
\(【考点】\)
容斥原理
\(【做法】\)
题目中含有多种限制条件,考虑容斥原理。首先枚举一共最多有\(i\)种颜色涂在了矩阵中,设其答案为\(f[i]\),则最终答案\(ans\)为:
\[ans=\sum\limits_{i=1}^{c}(-1)^{c-i} \cdot \bigg({C \atop i}\bigg)\cdot f[i]
\]
对于每一个\(f[i]\),可以考虑二次容斥,枚举有\(j\)列涂颜色。其中\((i+1)^j-1\)即为\(i+1\)种颜色(可以不涂)涂到\(j\)列的方案数(最后减去全不涂颜色的方案)。即:
\[f[i]=\sum\limits_{j=1}^{m}(-1)^m \cdot \bigg( {m \atop j}\bigg)\cdot \prod\limits_{k=1}^{n} ((i+1)^j-1)
\]
最后预处理组合数,使用快速幂就可以在\(O(mc\log n)\)的复杂度内求出答案。
\(【代码】\)
#include<cstdio>
#include<iomanip>
using namespace std;
typedef long long ll;
const int N=4e2+50;
const ll MOD=1000000007;
ll f[N];
ll C[N][N];//组合数
int n,m,c;
inline void Calc()//预处理组合数
{
for(int i=0;i<=400;i++) C[i][0]=1;
for(int i=1;i<=400;i++){
for(int j=1;j<=i;j++){
C[i][j]=C[i-1][j-1]+C[i-1][j];
C[i][j]%=MOD;
}
}
}
inline ll Fast_Pow(ll a,ll b,ll p)//快速幂
{
ll ans=1,base=a;
while(b>0){
if(b&1){
ans*=base;
ans%=p;
}
base*=base;
base%=p;
b>>=1;
}
return ans;
}
inline ll Work(int x)//二次容斥求f[x]
{
ll k=1;
ll res=0;
ll tmp=1;
for(int j=1;j<=m;j++){
if((m-j)%2==0) k=1;
else k=-1;
tmp*=(ll)x+1;
tmp%=MOD;
res+=k*C[m][j]*Fast_Pow((tmp-1+MOD)%MOD,(ll)n,MOD);
res=(res+MOD)%MOD;
}
return res;
}
int main()
{
scanf("%d%d%d",&n,&m,&c);
Calc();
ll k=1;
ll ans=0;
for(int i=1;i<=c;i++){
if((c-i)%2==0) k=1;//判断当前方案是容是斥
else k=-1;
ans+=k*C[c][i]*Work(i);
ans=(ans+MOD)%MOD;//ans可能为负数,应当加MOD后%MOD
}
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号