有这么一类问题,要求统计一棵树上与子树相关的某些信息,比如:在一棵所有节点被染色的树上,统计每棵子树上出现次数最多的颜色编号之和。

很自然的可以想到用DFS序+主席树去求解,但是编码复杂度很高;

然后我们可以想到DFS序+莫队解决,然而$O(n\sqrt{n})$的时间复杂度在数据较大的时候容易TLE;

有没有更优美一点的解法呢?DSU On Tree(据说叫树上启发式合并)可以以较小的编码复杂度在$O(n\log n)$的时间复杂度完成对于所有子树信息的统计。

先上模板

 void dsu(LL k,LL f,LL x){
fore(i,k,v)
if(v!=f&&v!=son[k])dsu(v,k,);//统计轻儿子所在的子树,不保留信息
if(son[k])
dsu(son[k],k,),hs=son[k];//统计重儿子所在子树,保留信息
calc(k,f,);//统计所需信息
hs=;ans[k]=sum;
if(!x)calc(k,f,-),mx=sum=;//如果是轻儿子,则清除信息
}

看上去除了每次保留了重儿子的信息和暴力也没啥区别。。。

但是实际上,只有dfs到轻边时,才会将轻边的子树中合并到上一级的重链,树剖后的每条链上至多有$log n$条轻边,所以每一个节点最多向上合并$log n$次,每次统计的复杂度是$O(n)$,整体复杂度为$O(nlog n)$

在竞赛中,DSU On Tree 是一个不错的trick,可以有效减少代码复杂度。然而其缺陷也是明显的,这种算法的适用范围非常狭窄,仅适用于对于子树信息的统计,并且不滋磁修改操作。


来看一道模板题:传送门

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 ≤ 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

题目大意:给出一棵树,每一个节点有一个颜色,统计以每一个节点为根的子树中出现次数最多的颜色(可能不止一种)的编号和。

套上面的模板就好啦。

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define foru(i,x,y) for(LL i=x;i<=y;i++)
#define fore(i,x,v) for(LL i=head[x],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
using namespace std;
typedef long long LL;
const LL N=2e5+;
struct edge{LL to,nxt;}e[N*];
LL head[N],siz[N],son[N],ans[N],cnt[N],c[N],n,sum,hs,mx,ne;
void add(LL a,LL b){e[++ne]=(edge){b,head[a]};head[a]=ne;} void dfs(LL k,LL f){
siz[k]=;son[k]=;
fore(i,k,v){
if(v==f)continue;
dfs(v,k);
siz[k]+=siz[v];
if(siz[v]>siz[son[k]])son[k]=v;
}//剖分轻重儿子
} void calc(LL k,LL f,LL x){
cnt[c[k]]+=x;
if(cnt[c[k]]>mx)sum=c[k],mx=cnt[c[k]];
else if(cnt[c[k]]==mx)sum+=c[k];
fore(i,k,v)
if(v!=f&&v!=hs)calc(v,k,x);
} void dsu(LL k,LL f,LL x){
fore(i,k,v)
if(v!=f&&v!=son[k])dsu(v,k,);//统计轻儿子所在的子树,不保留信息
if(son[k])
dsu(son[k],k,),hs=son[k];//统计重儿子所在子树,保留信息
calc(k,f,);//统计所需信息
hs=;ans[k]=sum;
if(!x)calc(k,f,-),mx=sum=;//如果是轻儿子,则清除信息
} int main(){
LL u,v;
scanf("%I64d",&n);
foru(i,,n)scanf("%I64d",&c[i]);
foru(i,,n-){
scanf("%I64d%I64d",&u,&v);
add(u,v);add(v,u);
}
dfs(,);
dsu(,,);
foru(i,,n)printf("%I64d ",ans[i]);printf("\n");
}

DSU On Tree——Codeforces 600E(E. Lomsat gelral)的更多相关文章

  1. codeforces 600E E. Lomsat gelral (线段树合并)

    codeforces 600E E. Lomsat gelral 传送门:https://codeforces.com/contest/600/problem/E 题意: 给你一颗n个节点的树,树上的 ...

  2. Codeforces 600 E - Lomsat gelral

    E - Lomsat gelral 思路1: 树上启发式合并 代码: #include<bits/stdc++.h> using namespace std; #define fi fir ...

  3. Codeforces 600 E. Lomsat gelral (dfs启发式合并map)

    题目链接:http://codeforces.com/contest/600/problem/E 给你一棵树,告诉你每个节点的颜色,问你以每个节点为根的子树中出现颜色次数最多的颜色编号和是多少. 最容 ...

  4. 「CF 600E」 Lomsat gelral

    题目链接 戳我 \(Describe\) 给出一棵树,每个节点有一个颜色,求每个节点的子树中颜色数目最多的颜色的和. \(Solution\) 这道题为什么好多人都写的是启发式合并,表示我不会啊. 这 ...

  5. 树上启发式合并(dsu on tree)学习笔记

    有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...

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

    题目链接:http://codeforces.com/problemset/problem/600/E n个点的有根树,以1为根,每个点有一种颜色.我们称一种颜色占领了一个子树当且仅当没有其他颜色在这 ...

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

    dsu on tree板子题.这个trick保证均摊O(nlogn)的复杂度,要求资瓷O(1)将一个元素插入集合,清空集合时每个元素O(1)删除.(当然log的话就变成log^2了) 具体的,每次先遍 ...

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

    题目链接 dsu on tree详见这. \(Description\) 给定一棵树.求以每个点为根的子树中,出现次数最多的颜色的和. \(Solution\) dsu on tree模板题. 用\( ...

  9. 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 verte ...

随机推荐

  1. SoapUI substring

  2. Neo4j安装配置(mac)

    Neo4j安装配置(mac) 1.下载APP 注意:无需配置变量 下载地址:https://neo4j.com/download/ 2.安装程序并启动 3.创建数据库(local) 选择版本 4.启动 ...

  3. Codeforces Round #619 (Div. 2)E思维+二维RMQ

    题:https://codeforces.com/contest/1301/problem/E 题意:给个n*m的图形,q个询问,每次询问问询问区间最大的合法logo的面积是多少 分析:由于logo是 ...

  4. C++queue的使用

    C++队列是一种容器适配器,提供了一种先进先出的数据结构. 队列(queue)模板类定义在<queue>头文件中 基本操作: 定义一个queue变量:queue<Type> q ...

  5. 201771010123汪慧和《面向对象程序设计JAVA》第七周实验总结

    一.理论部分 1.继承 如果两个类存在继承关系,则子类会自动继承父类的方法和变量,在子类中可以调用父类的方法和变量,如果想要在子类里面做一系列事情,应该放在父类无参构造器里面,在java中,只允许单继 ...

  6. 洛谷 P4342 [IOI1998]Polygon

    题目传送门 解题思路: 一道环形dp,只不过有个地方要注意,因为有乘法,两个负数相乘是正数,所以最小的数是负数,乘起来可能比最大值大,所以要记录最小值(这道题是紫题的原因). AC代码: #inclu ...

  7. tar.xz文件

    创建或解压tar.xz文件的方法 习惯了 tar czvf 或 tar xzvf 的人可能碰到 tar.xz也会想用单一命令搞定解压或压缩.其实不行 tar里面没有征对xz格式的参数比如 z是针对 g ...

  8. idea代理上网

    idea 代理上网 浏览器能够上网,idea无法下载jar 浏览器无法上网则配置浏览器代理 --------- start //------------------------浏览器代理完毕 idea ...

  9. unity学习 5.x依赖打包和解包

    unity5已经封装好了接口,所以依赖打包并没有那么神秘和复杂了. 打包: 1.定义好资源的assetBundleName 2.BuildPipeline.BuildAssetBundles,指定资源 ...

  10. Mysql 环境部署

    1.Window 1.1 下载软件: https://dev.mysql.com/downloads/mysql/ 依次点击上图 红色框中按钮 1.2 安装软件 1.2.1 解压软件 正常解压即可  ...