[CSAcademy]Connected Tree Subgraphs

题目大意:
  给你一棵n个结点的树,求有多少种染色方案,使得染色过程中染过色的结点始终连成一块。

思路:
  树形DP。
  设f[x]表示先放x时,x的子树中的染色方案数,y为x的子结点。
  则f[x]=prod{f[y]}*(size[x]-1)!/prod{size[y]}。
  现在我们改变f[x]的含义,让其表示先放x时,整棵子树的方案数。
  考虑根的转移对答案做出的贡献。
  假设我们从x转移到y,那么就要把y从x中剔除,
  设剔除y后的f[x]为t,则t=f[x]*size[y]*(size[x]-1-size[y])!/f[y]/(size[x]-1)!。
  这是我们要把x子树中的方案数,也就是t算入f[y]中。
  f[y]=f[y]*t*(n-1)!/(size[y]-1)!/(n-1-size[y])!。
  把t代入,发现f[y]=f[x]*(n-1-size[y])!*size[y]!/(size[y]-1)!/(n-size[y])!。
  时间复杂度O(n)。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<vector>
 4 typedef long long int64;
 5 inline int getint() {
 6     register char ch;
 7     while(!isdigit(ch=getchar()));
 8     register int x=ch^'0';
 9     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
10     return x;
11 }
12 const int N=100001;
13 const int mod=1e9+7;
14 std::vector<int> e[N];
15 inline void add_edge(const int &u,const int &v) {
16     e[u].push_back(v);
17     e[v].push_back(u);
18 }
19 void exgcd(const int &a,const int &b,int &x,int &y) {
20     if(!b) {
21         x=1;
22         y=0;
23         return;
24     }
25     exgcd(b,a%b,y,x);
26     y-=a/b*x;
27 }
28 inline int inv(const int &x) {
29     int ret,tmp;
30     exgcd(x,mod,ret,tmp);
31     return (ret%mod+mod)%mod;
32 }
33 int f[N],fact[N],size[N];
34 void dfs(const int &x,const int &par) {
35     f[x]=size[x]=1;
36     for(unsigned i=0;i<e[x].size();i++) {
37         const int &y=e[x][i];
38         if(y==par) continue;
39         dfs(y,x);
40         size[x]+=size[y];
41         f[x]=(int64)f[x]*f[y]%mod*inv(fact[size[y]])%mod;
42     }
43     f[x]=(int64)f[x]*fact[size[x]-1]%mod;
44 }
45 void move(const int &x,const int &par) {
46     for(unsigned i=0;i<e[x].size();i++) {
47         const int &y=e[x][i];
48         if(y==par) continue;
49         f[y]=(int64)f[x]*fact[size[1]-1-size[y]]%mod*fact[size[y]]%mod*inv(fact[size[y]-1])%mod*inv(fact[size[1]-size[y]])%mod;
50         move(y,x);
51     } 
52 }
53 int main() {
54     const int n=getint();
55     fact[0]=1;
56     for(register int i=1;i<n;i++) {
57         fact[i]=(int64)fact[i-1]*i%mod;
58     }
59     for(register int i=1;i<n;i++) {
60         add_edge(getint(),getint());
61     }
62     int ans=0;
63     dfs(1,0);
64     move(1,0);
65     for(register int i=1;i<=n;i++) {
66         ans=(ans+f[i])%mod;
67     }
68     printf("%d\n",ans);
69     return 0;
70 }

 

posted @ 2017-12-27 11:08  skylee03  阅读(173)  评论(0编辑  收藏  举报