codeforces 600 E. Lomsat gelral (dsu on the tree)

E. Lomsat gelral
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given a rooted tree with root in vertex 1. Each vertex is coloured in some colour.

Let's call colour c dominating in the subtree of vertex v if there are no other colours that appear in the subtree of vertex v more times than colour c. So it's possible that two or more colours will be dominating in the subtree of some vertex.

The subtree of vertex v is the vertex v and all other vertices that contains vertex v in each path to the root.

For each vertex v find the sum of all dominating colours in the subtree of vertex v.

Input

The first line contains integer n (1 ≤ n ≤ 105) — the number of vertices in the tree.

The second line contains n integers ci (1 ≤ ci ≤ n), ci — the colour of the i-th vertex.

Each of the next n - 1 lines contains two integers xj, yj (1 ≤ xj, yj ≤ n) — the edge of the tree. The first vertex is the root of the tree.

Output

Print n integers — the sums of dominating colours for each vertex.

Examples
input
4
1 2 3 4
1 2
2 3
2 4
output
10 9 3 4
input
15
1 2 3 1 2 3 3 1 1 3 2 2 1 2 3
1 2
1 3
1 4
1 14
1 15
2 5
2 6
2 7
3 8
3 9
3 10
4 11
4 12
4 13
output
6 5 4 3 2 3 3 1 1 3 2 2 1 2 3


题目大意:求每个点的子树中颜色的出现数量最多的颜色的和。

题解:dsu on the tree

树上启发式合并。其实是一个很暴力的做法,用来解决统计树上一个节点的子树中具有某种特征的节点数,或者子树中的某个值。

做法非常的暴力,就是如果是轻儿子,每次统计完了就将影响消除,如果是重儿子就保留。每次统计答案的时候,先计算轻儿子,在计算中儿子。

最后均摊的时间复杂度是O(nlogn),时间复杂度的证明我也不太会,想明白了再来写。。。。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 200003
#define LL long long 
using namespace std;
int n,m,point[N],v[N],nxt[N],mark[N];
int size[N],son[N],tot;
LL cx,sum,num[N],ans[N],col[N];
void add(int x,int y)
{
	tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
	tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void solve(int x,int f)
{
	size[x]=1;
	for (int i=point[x];i;i=nxt[i]) {
		if (v[i]==f) continue;
	    solve(v[i],x);
		size[x]+=size[v[i]];
		if (size[v[i]]>size[son[x]]) son[x]=v[i];
	}
}
void calc(int x,int f,int val)
{
	num[col[x]]+=(LL)val;
	if (val>0&&num[col[x]]>=cx) {
	 if (num[col[x]]>cx) sum=0,cx=num[col[x]];
	 sum+=(LL)col[x];
	}
	for (int i=point[x];i;i=nxt[i])
	 if (v[i]!=f&&!mark[v[i]]) calc(v[i],x,val);
}
void dfs(int x,int f,bool k=0)
{
	for (int i=point[x];i;i=nxt[i])
	 if (v[i]!=f&&v[i]!=son[x]) dfs(v[i],x,0);
	if (son[x]) dfs(son[x],x,1),mark[son[x]]=1;
	calc(x,f,1);
	ans[x]=sum;
	if (son[x]) mark[son[x]]=0;
	if (!k) calc(x,f,-1),cx=sum=0;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%I64d",&col[i]);
	for (int i=1;i<n;i++) {
		int x,y; scanf("%d%d",&x,&y);
		add(x,y);
	}
	solve(1,0);
	dfs(1,0,0);
	for (int i=1;i<=n;i++) printf("%I64d ",ans[i]);
	printf("\n");
} 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值