[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;
}
posted @ 2020-11-23 20:18  lxzy  阅读(217)  评论(0)    收藏  举报