文章目录

  • 解析
    • step1:树剖
    • step2:求出轻儿子的答案(不继承)
    • step3:求出重儿子的答案(继承)
    • step4:加入自己的答案、合并轻儿子的答案并记录答案
    • step5:清空
  • 复杂度分析
  • 代码

所谓树上启发式合并,就是在树上进行启发式合并

(逃)

解析

通过很妙的操作将暴力的复杂度优化到log级别
可以解决对子树的一些离线静态问题
大概讲一下流程:

step1:树剖

先剖一下确定重儿子为后来的dsu做准备
顺便求出size和dfs序等信息
注意这个dfs序不一定要先搜重儿子
所以直接在一个dfs函数里解决就可以

void dfs0(int x,int f){siz[x]=1;L[x]=++tim;dfn[tim]=x;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f) continue;dfs0(to,x);if(siz[hson[x]]<siz[to]) hson[x]=to;siz[x]+=siz[to];}R[x]=tim;
}

step2:求出轻儿子的答案(不继承)

接下来的操作都在dsu的主函数中:

void dfs(int x,int f,bool kep)

kep表示当前结点的信息是否需要传给父亲

先递归把所有轻儿子的答案求出来
不继承给父亲

for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f||to==hson[x]) continue;dfs(to,x,0);}

step3:求出重儿子的答案(继承)

求出重儿子的答案,并继承到父亲接着用

if(hson[x]) dfs(hson[x],x,1);

step4:加入自己的答案、合并轻儿子的答案并记录答案

把自己的答案加进来
并利用求好的dfs序
暴力把所有轻儿子的答案合并到父亲
最后记录一下当前的答案

add(x);for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f||to==hson[x]) continue;for(int j=L[to];j<=R[to];j++) add(dfn[j]);}ans[x]=res;

step5:清空

如果不需要传给父亲,就把整个子树的信息暴力清空

if(!kep){for(int i=L[x];i<=R[x];i++) del(dfn[i]);res=mx==0;}

复杂度分析

考虑一个结点被遍历到几次
注意到一个结点每作为一个轻儿子的子树,就会被多遍历一次
上面那个东西其实就是到根的轻链的数目
众所周知这玩意是log的
所以复杂度是非常优秀的nlogn
应该和树剖一样跑的很不满
当然如果单次合并答案复杂度不是O1那还要再乘合并复杂度

代码

题目传送门

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+100;
const int mod=998244353;
inline ll read() {ll x=0,f=1;char c=getchar();while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}
int n,m;
struct node{int to,nxt;
}p[N<<1];
int fi[N],cnt;
inline void addline(int x,int y){p[++cnt]=(node){y,fi[x]};fi[x]=cnt;return;
}
int siz[N],L[N],R[N],tim,hson[N],dfn[N];
int col[N],bac[N],mx;
ll ans[N],res;
void dfs0(int x,int f){siz[x]=1;L[x]=++tim;dfn[tim]=x;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f) continue;dfs0(to,x);if(siz[hson[x]]<siz[to]) hson[x]=to;siz[x]+=siz[to];}R[x]=tim;
}
inline void add(int p){int o=col[p];++bac[o];if(bac[o]>mx){mx=bac[o];res=o;}else if(bac[o]==mx) res+=o;return;
}void dfs(int x,int f,bool kep){for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f||to==hson[x]) continue;dfs(to,x,0);}if(hson[x]) dfs(hson[x],x,1);add(x);for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f||to==hson[x]) continue;for(int j=L[to];j<=R[to];j++) add(dfn[j]);}ans[x]=res;if(!kep){for(int i=L[x];i<=R[x];i++) bac[col[dfn[i]]]=0;res=mx=0;}return;
}
int main(){#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);#endifmemset(fi,-1,sizeof(fi));cnt=-1;n=read();for(int i=1;i<=n;i++) col[i]=read();for(int i=1;i<n;i++){int x=read(),y=read();addline(x,y);addline(y,x);}dfs0(1,0);dfs(1,0,1);for(int i=1;i<=n;i++) printf("%lld ",ans[i]);return 0;
}

模板:树上启发式合并(dsu on tree)相关推荐

  1. 树上启发式合并问题 ---- D. Tree and Queries[树上启发式合并+树状数组]

    题目链接 题目大意: 就是给你一棵树,树上每个节点都有一个颜色,在你mmm次询问每次询问给你一个节点uuu和一个数字kkk,问你在uuu这颗子树里面又少种颜色的结点个数是大于kkk; 解题思路: 看到 ...

  2. 树上启发式合并问题 ---- D. Tree Requests [状态压缩+树上启发式合并]

    题目链接 题目大意: 就是给你一颗树,树上每个点都有一个小写字母,现在给你m∈[1,5e5]m\in [1,5e5]m∈[1,5e5]次询问,每次询问给你一个u,和deep,问你在u这课子树中,距离1 ...

  3. [CF600] E. Lomsat gelral ( 经典树上启发式合并 dus on tree )

    题目链接:E. Lomsat gelral 题解 经典树上启发式合并的题目,按照正常暴力求时间复杂度为O(n2){O(n^2)}O(n2),很明显是有问题的.那么树上启发式合并是如何做的呢. 先说一些 ...

  4. [dsu on tree]树上启发式合并总结(算法思想及模板附例题练习)

    文章目录 前言 树上启发式合并 引入 算法思想 时间复杂度 模板 练习 例题:CF600E Lomsat gelral solution code CF208E Blood Cousins solut ...

  5. 【CF 600E】Lomsat gelral(树上启发式合并, dsu on tree, 静态链分治,模板题)

    Algorithm 名称:树上启发式合并, dsu on tree, 静态链分治 用处:一般用来解决一类不带修改的子树查询问题 核心思想为:利用重链剖分的性质优化子树贡献的计算. 前置知识:启发式合并 ...

  6. 树上启发式合并(dsu on tree)

    dsu on tree dsu \text{dsu} dsu一般指 disjoint set union \text{disjoint set union} disjoint set union,即并 ...

  7. 2019 ICPC 南昌 K. Tree(树上启发式合并,平衡树 treap)

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 题目链接 https://nanti.jisuanke.com/t/42586 Problem 给定一 ...

  8. 中石油训练赛 - Russian Dolls on the Christmas Tree(树上启发式合并/主席树)

    题目链接:点击查看 题目大意:给出一棵 n 个节点的树,以点 1 为根,现在对于每个节点作为根的子树求解:子树中有多少个编号不相交的连续子段,如:1 2 4 5 7,共有三个连续的段,分别为 [ 1 ...

  9. 山东理工大学第十二届ACM程序设计竞赛 - Cut the tree(树上启发式合并+线段树)

    题目链接:点击查看 题目大意:给一个具有 N 个节点的有根树,以 1 号节点为根,节点编号从 1 开始,点有点权.树的第 H 层权值为深度为 H 的所有点的点权之和.树的总权值为所有层权值的最大值.问 ...

最新文章

  1. java+jtextfield+取值_[求助]JTextfield 取值问题!
  2. mysql Error Code: 1005(errorno:121)解决
  3. Apache Cassandra和Apache Ignite:关系并置和分布式SQL
  4. Kubernetes中分布式存储Rook-Ceph部署快速演练
  5. JAVA遇见HTML——JSP篇(JavaBeans)
  6. img2txt(二)
  7. Qt——P6 QPushButton创建
  8. matlab中提示错误使用* BLAS loading error解决方法
  9. 洛谷——P1010 [NOIP1998 普及组] 幂次方
  10. automake连载--Linux下使用autoconfig automake进阶
  11. 在asp中实现自动缩放图片(推荐)
  12. android studio 模拟器 简书,Android Studio的模拟器genymotion
  13. C++开发 一个壁纸小软件
  14. 记录走过python的坑:明明安装了某个模块,却提示“no mudule named xxx“
  15. 创业者的心得:心有菩提手有刀,欲成舍利却成妖(转)
  16. 解决金仓数据库安装时安装VC++2013报错问题:不受信任提供程序信任的根证书中终止
  17. Java中类对象为空是什么意思?
  18. Unity中的GUI编程
  19. 简明扼要的HDFS元数据管理机制描述(NameNode和Secondary NameNode工作机制)
  20. 那些牛逼互联网公司里技术团队的博客

热门文章

  1. 《SAS编程与数据挖掘商业案例》学习笔记之一
  2. java 录屏_java 录屏 小工具源码(idea)
  3. python 空指针_Python&CType空指针错误
  4. java中JOptionPane类_java:JOptionPane类消息框总结
  5. 线程打印_面试题:用程序实现两个线程交替打印 0~100 的奇偶数
  6. 通达信版弘历软件指标_通达信软件指标编写基础教程,10个指标源码祝你股市一帆风顺...
  7. 3des加密 java php_php 3DES加密如何兼容Java
  8. 开发物体识别桌、_【课程总结】AR系统开发“秘籍”大揭秘!
  9. rsa算法c语言实现_数据结构与算法之线性表-顺序表实现(C语言版本)
  10. leetcode454. 四数相加 II(思路+详解)