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

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

时间: 2024-09-30 19:34:35

CodeForces 600E Lomsat gelral(线段树合并)的相关文章

codeforces 600E . 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'

CF600E Lomsat gelral(线段树合并)

link 题目大意:给以1为根的一棵树,求树上每个点子树中出现次数最多的权值(如果有多个就求他们的和) 对每个点开一个线段树维护子树内权值的桶,dfs时候线段树合并就行了. 因为最后线段树一共插入最多 \(O(n\log n)\) 个节点,每个节点最多会被合并一次,所以复杂度是 \(O(n\log n)\) 的. #include <cstdio> #include <vector> using namespace std; struct fuck { int maxval; lo

Codeforces 600E Lomsat gelral (Dsu On the Tree)

题目链接 Lomsat gelral 占坑--等深入理解了再来补题解-- 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define rep(i, a, b) for (int i(a); i <= (b); ++i) 6 7 typedef long long LL; 8 9 const int N = 600010; 10 11 int n; 12 int cc[N], col[N], sz[N], son[N];

CodeForces 600E. Lomsat gelral【树上启发式合并】

传送门 好像大家都是拿这道题作为树上启发式合并的板子题. 树上启发式合并,英文是 dsu on tree,感觉还是中文的说法更准确,因为这个算法和并查集(dsu)没有任何关系.一般用来求解有根树的所有子树的统计问题. 根据轻重儿子的各种性质,可以证明这个算法的时间复杂度为 \(O(nlogn)\),虽然看起来暴力的不行,但是却是一个很高效的算法. 算法的核心其实就是对于每个节点,先计算轻儿子,再计算重儿子,把自己和轻儿子的所有贡献累计到重儿子上去,如果自己是轻儿子,就把贡献清空. 如果掌握了树链

Codeforces 600E. Lomsat gelral(Dsu on tree学习)

题目链接:http://codeforces.com/problemset/problem/600/E n个点的有根树,以1为根,每个点有一种颜色.我们称一种颜色占领了一个子树当且仅当没有其他颜色在这个子树中出现得比它多.求占领每个子树的所有颜色之和. 我们都知道可以$BST$启发式合并从而完美${O(nlogn^{2})}$,这太丑陋了. 那么$Dsu~~on~~tree$是在干啥呢? 找出树中每一个节点的重儿子,统计答案的时候优先进入每一个点的所有轻儿子,之后再进入重儿子,目的是保留重儿子所

Codeforces.600E.Lomsat gelral(dsu on tree)

题目链接 dsu on tree参见这. \(Description\) 给定一棵树.求以每个点为根的子树中,出现次数最多的颜色的和. \(Solution\) dsu on tree模板题. 用sum[i]表示出现次数为i的颜色的和,cnt[i]表示出现次数为i的颜色有多少个(其实有个Max表示当前最多的次数,和tm[i]就好了),然后就这样了.. 再写一遍dsu on tree大体过程:(设当前点为x) 计算轻儿子子树的答案,并删掉轻儿子的贡献(大多数时候): 计算重儿子子树的答案,保留重儿

Codeforces 600E - Lomsat gelral 「$Dsu \ on \ tree$模板」

With $Dsu \ on \ tree$ we can answer queries of this type: How many vertices in the subtree of vertex $v$ has some property in $O (n \log n)$ time (for all of the queries)? 这题写的是轻重儿子(重链剖分)版本的 $Dsu \ on \ tree$ 具体流程如下: 每次先递归计算轻儿子,再单独递归重儿子,计算完后轻儿子的一些信息

CF600E Lomsat gelral 【线段树合并】

题目链接 CF600E 题解 容易想到就是线段树合并,维护每个权值区间出现的最大值以及最大值位置之和即可 对于每个节点合并一下两个子节点的信息 要注意叶子节点信息的合并和非叶节点信息的合并是不一样的 由于合并不比逐个插入复杂度高,所以应是\(O(nlogn)\)的 #include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #i

Codeforces Gym 101194G Pandaria (2016 ACM-ICPC EC-Final G题, 并查集 + 线段树合并)

题目链接  2016 ACM-ICPC EC-Final Problem G 题意  给定一个无向图.每个点有一种颜色. 现在给定$q$个询问,每次询问$x$和$w$,求所有能通过边权值不超过w的边走到$x$的点的集合中,哪一种颜色的点出现的次数最多. 次数相同时输出编号最小的那个颜色.强制在线. 求哪种颜色可以用线段树合并搞定. 关键是这个强制在线. 当每次询问的时候,我们先要求出最小生成树在哪个时刻恰好把边权值不超过$w$的边都用并查集合并了. 在做最小生成树的时候每合并两个节点,另外开一个