题目链接: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
41 2 3 41 22 32 4
Output
10 9 3 4
Input
151 2 3 1 2 3 3 1 1 3 2 2 1 2 31 21 31 41 141 152 52 62 73 83 93 104 114 124 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; }
原文地址:https://www.cnblogs.com/caijiaming/p/12000925.html