codeforces gym 100548f
題目如圖
題意是說給n個花,m種顏色,要求相鄰花顏色不能相同,總共有k種不同的顏色。
剛開始想的是從m種顏色裏選k種,然後第一朵花有k種選法,後面的都是k-1種,這樣就是C(m,k) * (k - 1) ^ (n - 1) * k,然後交的時候第二個一直wa,後來發現會這樣並不能保證有k種顏色,比如4朵花3種顏色可以是1,2,1,2,只有兩種顏色。所以後來的思路是從這個裏面減去不超過k - 1種顏色的選法就是答案,然後想的式子是C(m,k)((k - 1) ^ (n - 1) * k - C(k,k - 1) * (k - 1) * (k - 2) ^ (n - 1)),這個意思是k種顏色裏選k - 1種塗n朵花,第一朵是k - 1種塗法,後面都是k - 2種,所以就是 C(k,k - 1) * (k - 1) * (k - 2) ^ (n - 1),減去這種情況就是k種顏色的,後來發現這樣也有問題。比如n = 4,m = 4,k = 4的時候,假設顏色是從1到4,從k = 4種顏色裏選三種顏色會有1,2,3 , 1,2,4, 1,3,4 ,2,3,4這四種選法,其中在1,2,3這種情況會有1,2,1,2這種塗法,在1,2,4這種情況也會有1,2,1,2這種塗法,相當於把兩種顏色的算了兩遍。然後依次類推從五種顏色選四種會出現把三種顏色的算2遍,兩種顏色的算三遍。記f[i] = C(k,i) * i * (i - 1) ^ (n - 1),那麽不超過2種顏色的應該是f[2] - f[1],不超過三種顏色是f[3] - 不超過兩種的,不超過四種是f[4] - 不超過三種的,所以不超過k - 1種就是f[k - 1] - 不超過k - 2種的,可以列出式子為(f[k - 1] - (f[k - 2] - (f[k - 3]… - (f[2] - f[1])))),然後由(k - 1) ^ (n - 1) * k減去它,也就是這種形式C(m,k) * (∑(i = 2,k) (-1) ^ (k - i) * f[i])。
在算的過程中需要用到逆元,因為求C(n,m)需要用到除法,所以用快速冪處理,a的逆元就是pow(a,mod - 2)。然後要對C(k,i)進行打表,求出i從1到k的值,還需要在最外層對逆元從1到1000000打表,不然會超時。
代碼如下:
#include<stdio.h> #include<math.h> #include<set> using namespace std; typedef long long ll; const int mod = 1000000007; ll c[1000010],inverse[1000010]; ll quickPow(ll a,ll n){ if(n == 0) return 1; ll ans = quickPow(a,n / 2); ans=ans%mod; if(n % 2 == 0) return ans * ans % mod; return (((ans * ans) % mod) * a) % mod; } ll C(ll a,ll b){ ll ans=1; for(int i=1;i<=b;i++){ ans*=(a-i+1); ans%=mod; ans*=inverse[i]; ans%=mod; } return ans; } int main(){ int T; scanf("%d",&T); int t = 0; for(int i = 1;i <= 1000000;i++) inverse[i] = quickPow(i,mod - 2); while(T--){ t++; ll n,m,k; scanf("%lld%lld%lld",&n,&m,&k); c[1] = k; for(int i = 2;i <= k;i++){ c[i] = c[i - 1] * (k - i + 1) % mod; c[i] = c[i] * inverse[i] % mod; } ll ans = 0; int sign = pow(-1,k - 1); for(int i = 1;i <= k;i++){ ans = (ans + sign * i % mod * c[i] % mod * quickPow(i - 1, n - 1) % mod + mod) % mod; sign *= -1; } ans = ans * C(m,k) % mod; printf("Case #%d: %lld\n",t,ans); } }
codeforces gym 100548f