[51nod] 彩色树 树形dp

这道题是个人都会看得出来是一道树形Dp.

大概的思路就是计算每个点的贡献,也就是说计算有多少个点对经过该点,但事实上这样的计算非常麻烦,我们可以只计算不经过该点的点对数量(补集),这样子思路就非常显然了,据说可以优化到O(n),但吾辈太菜只能做O(nlogn)的

顺便吐槽一下,这道题是多校原题,而且名字还一样(出题人偷懒!)

 

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<cmath>
 6 
 7 using namespace std;
 8 const int N = 100005,Mod = 1e9 + 7,Inv = (Mod+1)>>1;
 9 typedef long long ll;
10 struct edge{
11     int pos,nx;
12 }e[N*2];
13 
14 ll res,n,num,size,head[N],color[N],son[N],cnt[N];
15 bool vis[N];
16 
17 void insert(int u,int v){
18     e[++num].pos=v;
19     e[num].nx=head[u];
20     head[u]=num;
21 }
22 
23 void dfs(int x,int fa){
24     int i;
25     son[x]=1;
26     ll z=cnt[color[x]],y=cnt[color[x]];
27     for(i=head[x];i;i=e[i].nx){
28         if(e[i].pos==fa) continue;
29         dfs(e[i].pos,x);
30         son[x]=(son[x]+son[e[i].pos])%Mod;
31         ll temp=(son[e[i].pos]-cnt[color[x]]+z+Mod)%Mod;
32         res=(res+((((temp-1ll+Mod)%Mod)*temp%Mod)*Inv)%Mod)%Mod;
33         z=cnt[color[x]];
34     }
35     cnt[color[x]]=(son[x]+y)%Mod;
36 }
37 
38 int main(){
39     int x,y,i;
40     scanf("%lld",&n);
41     for(i=1;i<=n;i++) {
42         scanf("%d",&color[i]);
43         if(!vis[color[i]]) size++,vis[color[i]]=true;
44     }
45     
46     for(i=1;i<n;i++){
47         scanf("%d%d",&x,&y);
48         insert(x,y);
49         insert(y,x);
50     }
51     
52     dfs(1,1);
53     
54     ll ans=(((1ll*n*(n-1))%Mod*size)%Mod*Inv)%Mod;
55     for(i=1;i<=n;i++){
56         if(vis[i]){
57             ll temp=(n-cnt[i])%Mod;
58             ans=(ans-(((temp*(temp-1))%Mod)*Inv)%Mod+Mod*2)%Mod;
59         }
60     }
61     
62     ans=(ans-res+Mod)%Mod;
63     
64     for(i=1;i<n;i++) ans=(ans*i)%Mod;
65     
66     printf("%lld",(ans*2)%Mod);
67     return 0;
68 }

 

posted @ 2017-12-02 08:22  FranceDisco  阅读(223)  评论(0编辑  收藏  举报