2014-2015 ACM-ICPC, Asia Xian Regional Contest F Color

时间:2021-03-13 04:24:49


        有n朵花排成一列,m种颜色,给这些花染色,使得恰好有k种颜色,且相邻的花颜色不同,问有多少种染色法。

        首先必须从m种颜色中选出k种,这部分有C(m,k)种选法。然后第一朵花可以染成k种颜色,后面的所有花都能染成k-1种颜色,也就是k*(k-1)*...*(k-1)。但是这样不能保证整个过程中k种颜色全部用到了。于是需要根据容斥原理做一下处理,减去用了k-1种颜色的情况,加上用了k-2种颜色的情况......另外计算过程中用到了组合数,可以逆元+暴力计算出来。


#include <bits/stdc++.h>
using namespace std;

#define ll long long

const int mod = 1e9+7;

const int maxk = 1000010;

ll fac[maxk];
ll inv[maxk];

ll Pow(ll a,int n){
    ll re=1;
    while(n){
        if(n&1){
            re*=a;
            re%=mod;
        }
        a*=a;
        a%=mod;
        n>>=1;
    }
    return re;
}

//组合数
ll C(int n,int k){
    if(n<=1000000){
        ll res=fac[n]*inv[k];
        res%=mod;
        res*=inv[n-k];
        res%=mod;
        return res;
    }
    ll res=inv[k];
    for(int i=0;i<k;i++){
        res*=(n-i);
        res%=mod;
    }
    return res;
}

void init(){
    fac[0]=1;
    for(int i=1;i<maxk;i++){
        fac[i]=fac[i-1]*i;
        fac[i]%=mod;
    }
    inv[0]=1;   //注意这个不能漏
    for(int i=1;i<maxk;i++){//费马小定理计算阶乘的逆元
        inv[i]=Pow(fac[i],mod-2);
    }
}

int main(){
    init();
    int t;
    cin>>t;
    int cas=0;
    while(t--){
        cas++;
        ll n,m,k;
        cin>>n>>m>>k;
        ll part1 = C(m,k);
        ll part2 = k*Pow(k-1,n-1);
        part2%=mod;
        int sign = -1;
        for(ll i=k-1;i>1;i--){
            ll tmp = i*Pow(i-1,n-1);
            tmp%=mod;
            tmp*=C(k,i);
            tmp%=mod;
            tmp*=sign;
            part2+=tmp;
            part2+=mod;
            part2%=mod;
            sign=-sign;
        }
        ll ans = part1*part2;
        ans %= mod;
        printf("Case #%d: ",cas);
        cout<<ans<<endl;
    }

    return 0;
}