2014ACM/ICPC亞洲區西安站 F題 color (組合數學,容斥原理)

bigbigship發表於2015-06-11

題目連結:傳送門

題意:

n個格子排成一行,我們有m種顏色,可以給這些格子塗色,保證相鄰的格子的顏色不同

問,最後恰好使用了k種顏色的方案數。

分析:

看完題目描述之後立馬想到了一個公式 :C(m,k)*k*(k-1)^(n-1),但是仔細分析了一下

這個公式的含義是相鄰的格子顏色不同,使用的顏色總數小於等於k的方案數,但是這個

公式可以幫忙我們衍生出來下面的公式,C(k,x)*x*(x-1)^(n-1),這個公式的含義是在這

k種顏色中再選出來x種使得相鄰的格子不同色最後的顏色數小於等於x,然後每一個集合

都有交們我們可以考慮用容斥來搞一下。

設 S = F[x]=C(k,x)*x*(x-1)^(n-1);

ans = C(m,k) * sigma{ (-1)^(k-i) * C(k,i) * i *(i - 1)^(n-1)} (1 <= i <= k)

程式碼如下:

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

typedef long long LL;

const LL mod = 1e9+7;

const int maxn = 1e6+10;

LL n,m,k;

LL c[maxn],inv[maxn];

LL quick_mod(LL a,LL b){
    LL ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans ;
}

inline LL get_inverse(LL x){ //(a/b) % c = a*inv[b] %c if(c is a prime number) inv[b] = (b^(c-2))%c;
    return quick_mod(x,mod-2);
}

void init(){//將[1,1e6+10]的逆元預處理出來
    for(LL i=1;i<maxn;i++)
        inv[i]=get_inverse(i);
}

void get_combine(LL n){//得到組合數
    c[0]=1;
    for(LL i=1;i<=k;i++){
        c[i]=(c[i-1]*(n-i+1)%mod)*inv[i]%mod;
    }
}

inline LL calc(LL x){// x*C(k,x)*(x-1)^(n-1)
    return (c[x]*x%mod)*quick_mod(x-1,n-1)%mod;
}

int main(){
    init();
    int t,cas=1;
    scanf("%d",&t);
    while(t--){
        scanf("%lld%lld%lld",&n,&m,&k);
        get_combine(m);
        LL ans = c[k],ans1=0,tag=1;
        get_combine(k);
        for(LL i=k;i>=1;i--){
            ans1=(ans1+tag*calc(i)+mod)%mod;
            tag=-tag;
        }
        ans=ans*ans1%mod;
        printf("Case #%d: %lld\n", cas++, ans);
    }
    return 0;
}


 

 

相關文章