CodeForces 600E Lomsat gelral(线段树合并)

题目链接:http://codeforces.com/problemset/problem/600/E

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
题目大意:输入N,有N个结点,接下来N个数代表每个结点的颜色,接下来N-1行,每行两个数u v 代表他们之间有边,保证是一颗树,规定根为1
问你以每个结点为根,出现颜色最多的数的值的和为多少,(可能有多个数出现一样多)
思路:完全是一道线段树合并的板子题,打比赛的时候没时间看,补题的时候想过多颗树合并,但是觉得会超时,就没有写,看题解,发现就是线段树合并
这里给出复杂度,假设有n个结点,值域是m 则复杂度为:O(nlogm+N),证明我也不清楚 ,在这里是以权值建树
看代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long LL;
#define lson tr[now].l
#define rson tr[now].r
const int maxn=1e5+5;
const int maxm=50;
LL N;
LL cnt=0,num=0;
LL a[maxn];
LL head[maxn];
LL root[maxn];
LL anss[maxn];
struct Edge
{
    LL to,nxt;
}e[maxn<<1];
struct Tree
{
    LL l,r,sum,val,ans;//左右儿子,该种颜色的个数 ,对应的值 此时的答案
}tr[maxn<<5];
void add_edge(LL u,LL v)
{
    e[++cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}
void Push_up(LL now)
{
    if(tr[lson].sum>tr[rson].sum)//个数最多的在左子树
    {
        tr[now].ans=tr[lson].ans;
        tr[now].sum=tr[lson].sum;
        tr[now].val=tr[lson].val;
    }
    if(tr[lson].sum<tr[rson].sum)//个数最多的在右左子树
    {
        tr[now].ans=tr[rson].ans;
        tr[now].sum=tr[rson].sum;
        tr[now].val=tr[rson].val;
    }
    if(tr[lson].sum==tr[rson].sum)//个数最多的在左右子树
    {
        tr[now].ans=tr[lson].ans+tr[rson].ans;//答案是总和
        tr[now].sum=tr[lson].sum;//个数不变
        tr[now].val=tr[lson].val;//值无所谓
    }
}
LL Merge(LL x,LL y,LL l,LL r)
{
    if(!x) return y;//另一颗数为空 合并之后肯定还是本身
    if(!y) return x;
    if(l==r)//到了叶子节点
    {
        tr[x].sum+=tr[y].sum;//更新和
        tr[x].ans=l;
        tr[x].val=l;
        return x;
    }
    LL mid=(l+r)>>1;
    tr[x].l=Merge(tr[x].l,tr[y].l,l,mid);
    tr[x].r=Merge(tr[x].r,tr[y].r,mid+1,r);
    Push_up(x);
    return x;
}
void Update(LL &now,LL l,LL r,LL pos,LL v)
{
    if(!now) now=++num;//注意num的初值是N
    if(l==r)
    {
        tr[now].sum+=v;
        tr[now].ans=l;
        tr[now].val=l;
        return ;
    }
    LL mid=(l+r)>>1;
    if(pos<=mid) Update(lson,l,mid,pos,v);
    else Update(rson,mid+1,r,pos,v);
    Push_up(now);
}
void dfs(LL x,LL pre)
{
    for(LL i=head[x];i!=-1;i=e[i].nxt)
    {
        LL v=e[i].to;
        if(v==pre) continue ;
        dfs(v,x);
        Merge(root[x],root[v],1,N);//把它的子树和本身进行合并
    }
    Update(root[x],1,N,a[x],1);//该节点本身也有值,所以得更新
    anss[x]=tr[root[x]].ans;
}
int main()
{
    scanf("%lld",&N);
    num=N;
    for(int i=1;i<=N;i++)
    {
        scanf("%lld",&a[i]);
        root[i]=i;//每个结点对应一棵树
        head[i]=-1;
    }
    for(int i=1;i<N;i++)
    {
        LL u,v;scanf("%lld%lld",&u,&v);
        add_edge(u,v);add_edge(v,u);
    }
    dfs(1,0);
    for(int i=1;i<=N;i++) printf("%lld ",anss[i]);cout<<endl;
    return 0;
}

 

上一篇:实习生4面美团Java岗,已拿offer!(框架+多线程+集合+JVM)


下一篇:QT实现可扩展对话框