2014ACM/ICPC亞洲區西安站 F題 color (組合數學,容斥原理)
阿新 • • 發佈:2019-02-18
題目連結:傳送門
題意:
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; }