[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 }