[acmm week12]染色(容斥定理+组合数+逆元)

1003 染色
     
 
Time Limit: 1sec    Memory Limit:256MB
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 }

 

posted @ 2018-11-27 20:07 拦路雨偏似雪花 阅读(...) 评论(...) 编辑 收藏