题目链接:LA-7040
题意为用m种颜色给n个格子染色。问正好使用k种颜色的方案有多少。
首先很容易想到的是\( k * (k-1)^{n-1}\),这个算出来的是使用小于等于k种颜色给n个方格染色的方案数。
我们希望求得的是使用正好k种颜色给n个方格染色的方案数,简单的想法是,直接减去小于等于k-1种颜色的方案数。
但是,要计算使用小于等于k-1种颜色染色的方案数,不能直接减去\(C_{k}^{k-1} * (k-1) * (k-2)^{n-1}\),原因是会有重复的部分。
我们用\(S_{k}\)表示使用小于等于k种颜色给n个格子染色的方案数。
则我们希望求出的答案可以用\(C_m^k * (S_k- \bigcup _{i=1} ^ {C_k^{k-1}} S_{k-1})\)来表示。
于是问题变成了求\( \bigcup _{i=1} ^ {C_k^{k-1}} S_{k-1}\),因为有重复,自然而然的我们想到容斥原理。
仔细思考后(或者列表格),发现所有\(S_{k-1}\)两两相交的并等于\(S_{k-2}\),所有\(S_{k-1}\)三三相交的并等于\(S_{k-3}\),以此类推,减加减加即可。
代码如下:
1 #include<cstring> 2 #include<cstdio> 3 #include<queue> 4 #include<algorithm> 5 #include<set> 6 #include<cmath> 7 using namespace std; 8 typedef long long LL; 9 const LL MAXK=1000000; 10 const LL MOD=1e9+7; 11 12 LL CM[MAXK+10],CK[MAXK+10],inv[MAXK+10]; 13 LL extgcd(LL a,LL b,LL &x,LL &y) 14 { 15 LL d=a; 16 if(b!=0) 17 { 18 d=extgcd(b,a%b,y,x); 19 y-=(a/b)*x; 20 } 21 else { x=1; y=0; } 22 return d; 23 } 24 //快速幂 25 //求x^n%mod 26 LL powMod(LL x,LL n,LL mod) 27 { 28 LL res=1; 29 while(n>0) 30 { 31 if(n&1) res=res*x % mod; 32 x=x*x % mod; 33 n>>=1; 34 } 35 return res; 36 } 37 //求逆元 38 //a和m应该互质 39 LL modInverse(LL a,LL m) 40 { 41 LL x,y; 42 extgcd(a,m,x,y); 43 return (m+x%m)%m; 44 } 45 LL n,m,k; 46 void init() 47 { 48 CM[0]=1; 49 for(LL i=0;i<=k-1;i++) 50 CM[i+1]=CM[i]*(m-i) %MOD *inv[i+1] % MOD; 51 CK[0]=1; 52 for(LL i=0;i<=k-1;i++) 53 CK[i+1]=CK[i]*(k-i) %MOD *inv[i+1] % MOD; 54 } 55 LL f(LL n,LL k) 56 { 57 LL ans = 0; 58 LL flag=1; 59 for(LL i=k;i>=1;i--) 60 { 61 ans = (flag * CK[i] % MOD * i % MOD * powMod(i-1,n-1,MOD) % MOD +ans + MOD ) % MOD; 62 flag*=-1; 63 } 64 ans = ans*CM[k]%MOD; 65 return ans; 66 } 67 int main() 68 { 69 #ifdef LOCAL 70 freopen("in.txt","r",stdin); 71 // freopen("out.txt","w",stdout); 72 #endif 73 for(LL i=1;i<=MAXK;i++) inv[i]=modInverse(i,MOD); 74 LL t; 75 scanf("%lld",&t); 76 for(LL tt=1;tt<=t;tt++) 77 { 78 scanf("%lld%lld%lld",&n,&m,&k); 79 init(); 80 printf("Case #%lld: %lld\n",tt,f(n,k)%MOD); 81 } 82 return 0; 83 }
来源:https://www.cnblogs.com/zarth/p/6623433.html