1. 程式人生 > >Gym - 100548F Color 組合數+容斥

Gym - 100548F Color 組合數+容斥

題意:給你N朵花,M種顏料(n,m<=1e9),要求給所有花染色,且相鄰的花不能用同樣的顏色,求出最後恰好用了k種

顏料的方案數(k<=1e5)

題解:當我們用至少k中染色的時候 f[k] = k*(k-1)^(n-1)  其中包括了 至少k-1中的,我們需要減去C(k,k-1)*f[k-1]  ,但是很明顯我們多減了一次至少f[k-2]次的,舉個栗子:假設有1 2 3 4 5,我們先減去1 3 4 5 在減 2 3 4 5 所以其中多減了一次 3 4 5的 所以要再加上y一次f[k-2]  以此類推 ,這就是一個容斥,但是n,k,很大我們怎麼辦的,C(n,m+1)=C(n,m)*(n-m)/(m+1) 因此我們就需要求逆元了

注意乘法運算啊,每次乘,都要取模,否則就超1e9了 坑

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=1e6+100;
const ll mod=1000000007;
ll C[N],inv[N],n,m,k;
ll ksm(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}
void init()
{
    for(int i=1;i<N;i++)
        inv[i]=ksm(i,mod-2);
}
void cal(ll x)
{
    C[0]=1;
    for(int i=1;i<=k;i++)
        C[i]=((C[i-1]*(x-i+1)%mod)*inv[i]%mod)%mod;
}
int main()
{
    init();
    int T,nn=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld%lld",&n,&m,&k);
        cal(k);
        ll ans=k*ksm(k-1,n-1)%mod;
        for(ll i=2;i<=k-1;i++)
        {
            if((k-i)%2) ans=(ans-C[i]*(i*ksm(i-1,n-1)%mod)%mod)%mod;
            else ans=(ans+C[i]*(i*ksm(i-1,n-1)%mod)%mod)%mod;
        }
        cal(m);
        printf("Case #%d: %lld\n",nn++,(C[k]*ans%mod+mod)%mod);
    }
    return 0;
}