Educational Codeforces Round 2 E. Lomsat gelral(树上启发式合并)

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 ≤ 10^5) — 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

 题意:有一棵n个节点的树,每个节点有一个颜色。求以每个节点为根的子树中出现次数最多的颜色编号之和(出现次数最多的颜色可能有多个)。

思路:用map记录每个子树中颜色出现的个数,用maxn[x]表示以x为根的子树中颜色出现次数的最大值,然后从叶子节点向上进行合并,合并的时候用到启发式合并,即从小集合向大集合中进行合并,这样总合并的时间复杂度为O(nlogn)。假设A集合为大集合,B集合为小集合,则具体合并方法为,将B的map中的每个元素加入到A集合中,如果加入A集合后A的map存储的值大于maxn[A],则更新maxn[A],并且更新A的答案,如果map的值等于maxn[A],显然这个颜色没有被加入答案,把这颜色的编号加入答案。

关于启发式合并时间复杂度方面的计算,可以这样理解:每一次从一个小集合合并到一个较大的集合,则这个集合的大小至少是原来小集合的两倍,这样最坏情况下会合并logn次,所以每个点最多会被合并logn次,总的复杂度就为nlogn。树上的启发式合并通常写法是把一个节点的所有轻儿子合并到重儿子上,而我的写法是直接用儿子节点和父节点两两进行比较,这样虽然可能不是时间最优,但是写法简洁。

#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#define LL long long

using namespace std;


unordered_map<int,int> mp[100010];
LL ans[100010];
int maxn[100010];
int a[100010];
vector<int> e[100010];



void dfs(int x,int fa)
{
    mp[x][a[x]] = 1;
    maxn[x] = 1;
    ans[x] = a[x];
    for(int i=0;i<e[x].size();i++)
    {
        int xx = e[x][i];
        if(xx == fa)
            continue;
        dfs(xx,x);
        if(mp[x].size() < mp[xx].size())
        {
            swap(mp[x],mp[xx]);
            swap(maxn[x],maxn[xx]);
            ans[x] = ans[xx];
        }
        for(auto it : mp[xx])
        {
            mp[x][it.first] += it.second;
            if(mp[x][it.first] > maxn[x])
            {
                maxn[x] = mp[x][it.first];
                ans[x] = it.first;
            }
            else if(mp[x][it.first] == maxn[x])
            {
                ans[x] += it.first;
            }
        }
    }
}

int main(void)
{
    int n,i,j;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(i=2;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        e[x].push_back(y);
        e[y].push_back(x);
    }

    dfs(1,-1);
    for(i=1;i<=n;i++)
        printf("%lld ",ans[i]);


    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值