dsu on tree详解
这个算法还是挺人性化的,没有什么难度 就是可能看起来有点晕什么的。
大体 思想是 利用重链刨分来优化子树内部的查询。
考虑一个问题要对每个子树都要询问一次。我们暴力显然是\(n^2\)的。
考虑一下优化这个过程,我们发现儿子的信息可以给父亲用但是不能给兄弟或兄弟里的儿子用。
如果是最大最小值我们只能暴力来搞 但如果是出现次数什么的我们可以利用捅差分来解决这个事情。
考虑我们每次先暴力扫轻儿子然后 再做重儿子然后再把轻儿子的代价加上算当前节点的代价然后再把轻儿子的代价给删掉。
我们发现轻儿子被加上删掉两次 而重儿子只做一次并且保留。
可以发现这样做的复杂度很低 考虑一个点到根有logn条轻边所以这样最坏一个点被暴力来回扫logn次 统计自身答案的时候被扫了1次。
最终复杂度为nlogn 说起来很容易但其实代码还是存在一些细节的 要想好再写。
例题:CF600ELomsat gelral
const int MAXN=100010;
int n,len,mx;
int a[MAXN],cnt[MAXN],root[MAXN],sz[MAXN],son[MAXN];
int lin[MAXN],nex[MAXN<<1],ver[MAXN<<1];ll ans[MAXN],w;
inline void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
inline void dfs(int x,int father)
{
sz[x]=1;
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
if(tn==father)continue;
dfs(tn,x);
sz[x]+=sz[tn];
if(sz[son[x]]<sz[tn])son[x]=tn;
}
}
inline void update(int x,int father,int op,int target)
{
cnt[a[x]]+=op;
if(op>0&&cnt[a[x]]>=mx)
{
if(cnt[a[x]]==mx)w+=a[x];
else w=a[x],mx=cnt[a[x]];
}
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
if(tn==father||tn==target)continue;
update(tn,x,op,target);
}
}
inline void dfs(int x,int father,int op)
{
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
if(tn==father||tn==son[x])continue;
dfs(tn,x,0);//处理轻儿子的答案且清除
}
if(son[x])dfs(son[x],x,1);//处理重儿子的答案
update(x,father,1,son[x]);//把轻儿子的代价加入
ans[x]=w;//答案
if(!op)update(x,father,-1,0),w=0,mx=0;//当前是轻儿子所以删掉
}
int main()
{
freopen("1.in","r",stdin);
n=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<n;++i)
{
int x,y;
x=read();y=read();
add(x,y);add(y,x);
}
dfs(1,0);
dfs(1,0,1);
for(int i=1;i<=n;++i)printf("%lld ",ans[i]);
return 0;
}
虽然这道题可以使用线段树合并来做但是那样对空间和时间的花销都是nlogn的 所以dsu on tree在空间上要优于线段树合并。
且 常数上也必然小于线段树合并。
我们只是关注与dsu on tree的思想 使用重链刨分来进行优化。
再来一道简单的题目来简单再看一下:CF208E Blood Cousins
求有多少个点和某个点的K级祖先相同。不难想到先求出K级祖先然后求出K级祖先子树内深度为x的点的个数。
关于K级祖先的求法:可以倍增+长链刨分优化实现O(1)但是仅对这道题就没什么必要了 询问和n同阶。
可以直接倍增搞,我们还有更快的方法:离线 我们dfs一个点然后加到栈中我们维护某条链上的点。
询问直接查栈中的从前往后第K个元素即可。
考虑第二问 求子树内深度为x的点的个数。显然 dsu on tree。
当然可以使用线段树合并,还有更快的方法:离线 开捅统计对于询问进行捅内外的差分。综上这道题被离线干成了O(n).
为了学习dsu on tree这里使用dsu on tree.
这道题 离线大法好 又得到了一个求树上K级祖先的方法 离线开栈。
const int MAXN=100010;
int n,len,mx,tot,top,m;
int a[MAXN],s[MAXN],cnt[MAXN],root[MAXN],sz[MAXN],son[MAXN];
int lin[MAXN],nex[MAXN],ver[MAXN],d[MAXN],ans[MAXN];
vector<pii>g[MAXN],w[MAXN];
inline void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
inline void dfs(int x,int father)
{
d[x]=d[father]+1;sz[x]=1;
s[++top]=x;
for(int i=0;i<g[x].size();++i)
{
int tn=g[x][i].F;
if(tn>d[father])continue;
int ss=s[top-tn];
w[ss].push_back(mk(d[x],g[x][i].S));
}
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
if(tn==father)continue;
dfs(tn,x);
sz[x]+=sz[tn];
if(sz[son[x]]<sz[tn])son[x]=tn;
}
--top;
}
inline void update(int x,int father,int op,int target)
{
cnt[d[x]]+=op;
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
if(tn==father||tn==target)continue;
update(tn,x,op,target);
}
}
inline void dfs(int x,int father,int op)
{
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
if(tn==father||tn==son[x])continue;
dfs(tn,x,0);
}
if(son[x])dfs(son[x],x,1);
update(x,father,1,son[x]);
for(int i=0;i<w[x].size();++i)
ans[w[x][i].S]+=cnt[w[x][i].F];
if(!op)update(x,father,-1,0);
}
int main()
{
freopen("1.in","r",stdin);
n=read();
for(int i=1;i<=n;++i)
{
int x=read();
if(!x)root[++tot]=i;
else add(x,i);
}
m=read();
for(int i=1;i<=m;++i)
{
int x,y;
x=read();y=read();
g[x].push_back(mk(y,i));
}
for(int i=1;i<=tot;++i)dfs(root[i],0);
for(int i=1;i<=tot;++i)dfs(root[i],0,0);//dsu on tree
rep(1,m,i)printf("%d ",ans[i]?ans[i]-1:0);
return 0;
}
dsu on tree详解的更多相关文章
- [CF1009F] Dominant Indices (+dsu on tree详解)
这道题用到了dsu(Disjoint Set Union) on tree,树上启发式合并. 先看了CF的官方英文题解,又看了看zwz大佬的题解,差不多理解了dsu on tree的算法. 但是时间复 ...
- 【算法】关于图论中的最小生成树(Minimum Spanning Tree)详解
本节纲要 什么是图(network) 什么是最小生成树 (minimum spanning tree) 最小生成树的算法 什么是图(network)? 这里的图当然不是我们日常说的图片或者地图.通常情 ...
- 二叉查找树(binary search tree)详解
二叉查找树(Binary Search Tree),也称二叉排序树(binary sorted tree),是指一棵空树或者具有下列性质的二叉树: 若任意节点的左子树不空,则左子树上所有结点的值均小于 ...
- BTree和B+Tree详解
https://www.cnblogs.com/vianzhang/p/7922426.html B+树索引是B+树在数据库中的一种实现,是最常见也是数据库中使用最为频繁的一种索引.B+树中的B代表平 ...
- ODT(old driver tree)详解(带例题)
文章目录 ODT简介 实现前提&&实现原理 初始化 split操作 assign操作 其它操作 区间第k小 区间加 区间所有数的k次方和 几道水题 ODT简介 ODT(old driv ...
- 数据结构31:树(Tree)详解
复制广义表数据结构中的树 树是数据结构中比较重要也是比较难理解的一类存储结构.本章主要主要围绕二叉树,对树的存储以及遍历做详细的介绍,同时还会涉及到有关树的实际应用,例如构建哈弗曼编码等. 由于树存储 ...
- 【2018.9.26】K-D Tree详解
网上对K-D-Tree的讲解不尽清晰,我学了很久都不会写,这里新开一文做一些讲解. 1.K-D-Tree是什么? K-DTree 即 K-Dimensional-Tree,常用来作空间划分及近邻搜索, ...
- dsu on tree (树上启发式合并) 详解
一直都没出过算法详解,昨天心血来潮想写一篇,于是 dsu on tree 它来了 1.前置技能 1.链式前向星(vector 建图) 2.dfs 建树 3.剖分轻重链,轻重儿子 重儿子 一个结点的所有 ...
- Ext.Net学习笔记22:Ext.Net Tree 用法详解
Ext.Net学习笔记22:Ext.Net Tree 用法详解 上面的图片是一个简单的树,使用Ext.Net来创建这样的树结构非常简单,代码如下: <ext:TreePanel runat=&q ...
随机推荐
- vscode F2无法使用
rope库可能存在bug 解决方法: "python.jediEnabled": false //自动补全用微软自带
- python numpy 保留满足指定条件的行
#arr_old 原来数组 #arr_new 保留后的数组 #>=mean+std 指定条件 arr_new = arr_old[arr_old[:,4]>=(mean+std),:]#筛 ...
- python面试题二:Python 基础题
1.位和字节的关系? Byte 字节 bit 位 1Byte = 8bit 2.b.B.KB.MB.GB 的关系? 1Byte = 8bit KB 1KB=1024B MB 1MB=1024KB GB ...
- celery 错误相关:Monkey-patching not on the main thread; threading.main_thread().join() will hang from a greenlet
/Users/wangpingyang/.pyenv/versions/3.7.2/lib/python3.7/site-packages/httprunner/__init__.py:5: Monk ...
- 《串并行数据结构与算法(SML语言)实验》题解
注意:本题解仅供参考学习,请勿直接抄袭代码,否则造成的后果和笔者无关. 第一题: 题意: 对n个数升序排序. 题解: 快排,不解释. 代码(省略了输入输出函数,下同): val n = getInt ...
- sql中in和exists的原理及使用场景。
在我们的工作中可能会遇到这样的情形: 我们需要查询a表里面的数据,但是要以b表作为约束. 举个例子,比如我们需要查询订单表中的数据,但是要以用户表为约束,也就是查询出来的订单的user_id要在用户表 ...
- Web Scraping using Python Scrapy_BS4 - using Scrapy and Python(2)
Scrapy Architecture Creating a Spider. Spiders are classes that you define that Scrapy uses to scrap ...
- 享元模式(c++实现)
享元模式 目录 享元模式 模式定义 模式动机 UML类图 源码实现 优点 缺点 模式定义 享元模式(Flyweight),运用共享技术有效的支持大量细粒度的对象. 模式动机 如果一个应用使用了大量的对 ...
- 图灵学院笔记-java虚拟机底层原理
Table of Contents generated with DocToc 一.java虚拟机概述 二.栈内存解析 2.1 概述 2.2 栈帧内部结构 2.2.1 我们来解析一下compute() ...
- 数据库-SQL查询语言(一)
SQL数据定义 DDL sql的DDL不仅能定义一组关系,还能定义每个关系的信息,包括: 每个关系的模式 每个属性的取值类型 完整性约束 每个关系的维护的索引集合 每个关系的安全性和权限信息 每个关系 ...