[点分治] Luogu P2664 树上游戏

 

 

 

题解

  • 对于树中的一点i,如果该点的颜色在该点到根这条链上是第一次出现,那么对于这棵树的其他点j(以i和j的lca为根),均能与i的子树(包括i)组成点对,i的颜色会对j的答案贡献size[i]
  • 然后点分治就可以解决了

 

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #define ll long long
 4 using namespace std;
 5 const int N=2e5+10,inf=1e8;
 6 int n,cnt=1,root,head[N],v[N],vis[N*2];
 7 ll q,sum,num,tot,Mx=inf,size[N],col[N],ans[N],p[N];
 8 struct edge { int to,from; }e[N*2];
 9 void clear(int x,int fa) { p[v[x]]=col[v[x]]=0; for (int i=head[x];i;i=e[i].from) if (!vis[i]&&e[i].to!=fa) clear(e[i].to,x); }
10 void insert(int x,int y)
11 {
12     e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt;
13     e[++cnt].to=x,e[cnt].from=head[y],head[y]=cnt;
14 }
15 void dfs1(int x,int fa)
16 {
17     size[x]=1,p[v[x]]++;
18     for (int i=head[x];i;i=e[i].from) if (e[i].to!=fa&&!vis[i]) dfs1(e[i].to,x),size[x]+=size[e[i].to];
19     if (p[v[x]]==1) sum+=size[x],col[v[x]]+=size[x];
20     p[x[v]]--;
21 }
22 void dfs2(int x,int fa)
23 {
24     p[v[x]]++;
25     if (p[v[x]]==1) sum-=col[v[x]],num++;
26     ans[x]+=sum+num*q;
27     for (int i=head[x];i;i=e[i].from) if (!vis[i]&&e[i].to!=fa) dfs2(e[i].to,x);
28     if (p[v[x]]==1) sum+=col[v[x]],num--;
29     p[v[x]]--;
30 }
31 void findroot(int x,int fa)
32 {
33     size[x]=1; ll mx=0;
34     for (int i=head[x];i;i=e[i].from) if (e[i].to!=fa&&!vis[i]) findroot(e[i].to,x),size[x]+=size[e[i].to],mx=max(mx,size[e[i].to]);
35     mx=max(mx,tot-size[x]);
36     if (mx<Mx) Mx=mx,root=x;
37 }
38 void change(int x,int fa,int k)
39 {
40     p[v[x]]++;
41     for (int i=head[x];i;i=e[i].from) if (e[i].to!=fa&&!vis[i]) change(e[i].to,x,k);
42     if (p[v[x]]==1) sum+=(ll)size[x]*k,col[v[x]]+=(ll)size[x]*k;
43     p[v[x]]--;
44 }
45 void solve(int x,int fa)
46 {
47     dfs1(x,fa),ans[x]+=sum-col[v[x]]+size[x];
48     for (int i=head[x];i;i=e[i].from) if (!vis[i]&&e[i].to!=fa)
49         p[v[x]]++,sum-=size[e[i].to],col[v[x]]-=size[e[i].to],change(e[i].to,x,-1),p[v[x]]--,q=size[x]-size[e[i].to],dfs2(e[i].to,x),p[v[x]]++,sum+=size[e[i].to],col[v[x]]+=size[e[i].to],change(e[i].to,x,1),p[v[x]]--;
50     sum=0,num=0,clear(x,fa);
51     for (int i=head[x];i;i=e[i].from) if (!vis[i]&&e[i].to!=fa) vis[i]=1,vis[i^1]=1,tot=size[e[i].to],Mx=inf,findroot(e[i].to,x),solve(root,0);
52 }
53 int main()
54 {
55     scanf("%d",&n);
56     for (int i=1;i<=n;i++) scanf("%d",&v[i]);
57     for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),insert(x,y);
58     tot=n,findroot(1,0),solve(root,0);
59     for (int i=1;i<=n;i++) printf("%lld\n",ans[i]);
60 }

 

posted @ 2019-08-09 21:29  BEYang_Z  阅读(179)  评论(0编辑  收藏  举报