[acmm week12]染色(容斥定理+组合数+逆元)
1003 染色
Description
今天离散数学课学了有关树的知识,god_v是个喜欢画画的人,所以他喜欢对于一棵树上色,且相邻节点不能染相同颜色,他有k种颜色,他希望他染色完后,这棵树上每种颜色都有,他想请教你有多少种染色方案?由于方案数过大,输出对1e9+7取模的结果。 Input
第一行 n,k表示树的节点和颜色数量(1<=k<=n<=100000) 第二行 n-1个数字,第i个数字表示第i+1个节点的父亲的编号,(f[i]<i+1)
Output
一个数字,表示方案数(%1e9+7) Sample Input
6 4
1 2 2 4 5
Sample Output
600
|
题解:
如果不限制k种颜色全部用上,则方案数为f[k]=k*(k-1)^(n-1)(根节点有k种选择,其他节点有k-1种选择)。限制后,我们可以用容斥定理:ans=f[k] - C(k-1,k)*f[k-1] + C(k-2,k)*f[k-2] - .... + (-1)^(k-1) * C(1,k) * f[1];
f:用快速幂求出
线性求C(i,k) (1<=i<=k):C(i,k) = C(i-1,k) * (i-k+1) / i; 设mod=10^9+7,由于最后答案要%mod,则除以i要转化为乘以i在%mod下的逆元。
线性求逆元: inv[i]=((mod-mod/i))*inv[mod%i]%mod;(inv[i]为i的逆元,emm这里也可以用费马小定理来求)
O(nlogn)
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 const LL mod=1e9 + 7; 6 const int N=100010; 7 LL c[N],inv[N]; 8 9 void cal_c(int k) 10 { 11 inv[1]=1; 12 for(LL i=2;i<=k;i++) 13 { 14 inv[i]=((mod-mod/i))*inv[mod%i]%mod; 15 } 16 c[0]=1; 17 for(LL i=1;i<=k;i++) c[i]=c[i-1]*(k-i+1)%mod*inv[i]%mod; 18 } 19 20 LL pow(LL x,LL y) 21 { 22 LL ans=1; 23 while(y) 24 { 25 if(y&1) ans=ans*x%mod; 26 x=x*x%mod; 27 y/=2; 28 } 29 return ans; 30 } 31 32 int main() 33 { 34 //freopen("a.in","r",stdin); 35 LL n,k; 36 scanf("%lld%lld",&n,&k); 37 cal_c(k); 38 LL ans=0,f=1,now; 39 for(LL i=k;i>=1;i--) 40 { 41 now=(((c[i]*i)%mod)*pow(i-1,n-1))%mod; 42 ans=(ans+f*now+mod)%mod; 43 f=-f; 44 } 45 printf("%lld\n",ans); 46 return 0; 47 }