标签:sample for with 左右子树 and examples upd end you
题目链接: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
4
1 2 3 4
1 2
2 3
2 4
10 9 3 4
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
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; }
CodeForces 600E Lomsat gelral(线段树合并)
标签:sample for with 左右子树 and examples upd end you
原文地址:https://www.cnblogs.com/caijiaming/p/12000925.html