题目链接: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