DFS序——树链剖分前驱知识
目录
定义:dfs序:每个节点在dfs深度优先遍历中的进出栈的时间序列。
性质:dfs序可以把一棵树区间化,即可以求出每个节点的管辖区间。
对于一棵树的dfs序而言,同一棵子树所对应的一定是dfs序中连续的一段。
dfs序的七个基本问题:
定义:dfs序:每个节点在dfs深度优先遍历中的进出栈的时间序列。
定义两个数组,in[x],out[x]。dfs从根结点开始,每个结点分别记录两个信息:in[x],out[x],in[x]为dfs进入结点x时的时间戳,out[x]为dfs离开结点x时的时间戳。
dfs序的基本代码:
void dfs(int x,int pre,int d){//L,R表示一个子树的范围 L[x]=++tot; dep[x]=d; for(int i=0;i<e[x].size();i++){ int y=e[x][i]; if(y==pre)continue; dfs(y,x,d+1); } R[x]=tot; }
dfs序就是:
性质:dfs序可以把一棵树区间化,即可以求出每个节点的管辖区间。
对于一棵树的dfs序而言,同一棵子树所对应的一定是dfs序中连续的一段。
如下图,DFS之后,那么树的每个节点就具有了区间的性质。
dfs序的七个基本问题:
ps:deep[x]为x的深度,l[x]为dfs序中x的位置,r[x]为dfs序中x子树的结束位置
1.点修改,子树和查询
在dfs序中,子树处于一个连续区间中。所以这题可以转化为:点修改,区间查询。用树状数组或线段树即可。
例:poj3321 Apple Tree
链接:邻接表数组实现详解
//dfs序+树状数组
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
using namespace std;
const int N = 500005;
struct Edge{ //邻接表——数组实现 (链式前向星)int to,next;
}edge[N];int head[N],tot,d[N];
int in[N],out[N]; //in[i]:dfs第一次进入顶点i的时间戳,in[i]为dfs离开该节点的时间戳
bool have[N];
int cnt;void init(){tot = 0;cnt = 0;memset(head,-1,sizeof(head));memset(d,0,sizeof(d));
}void addEdge(int u,int v,int &k){edge[k].to = v;edge[k].next = head[u]; //edge[k].next存储的是编号为i的边的(同样以u为起始点的)前一条边的编号 head[u] = k++; //最后一条以u为起始点的边的编号
}void dfs(int u){ //dfs序 in[u] = ++cnt;for(int k=head[u];k!=-1;k=edge[k].next){dfs(edge[k].to);}out[u] = cnt;
}int lowbit(int x){return x&(-x);
}int sum(int x){int res = 0;while(x){res+=d[x];x-=lowbit(x);}return res;
}void update(int x,int v){while(x<=cnt){d[x]+=v;x+=lowbit(x);}
}int main()
{int n,m;while(scanf("%d",&n)!=EOF){init();for(int i=0;i<n-1;i++){int u,v;scanf("%d%d",&u,&v);addEdge(u,v,tot);}dfs(1);/*for(int i=1;i<=n;i++){printf("%d %d\n",in[i],out[i]);}*/for(int i=1;i<=n;i++){have[i] = 1;update(in[i],1);}int q;scanf("%d",&q);while(q--){char op;int x;scanf(" %c%d",&op,&x); /*注意字符读入时,前面加上空格,这样可避免回车符的读入 当然字符也可以以字符串的形式读入,取第一个字符,这样前面就无需加空格了*/ //cout<<"op="<<op<<"x="<<endl;if(op=='Q'){printf("%d\n",sum(out[x])-sum(in[x]-1)); //[in[x],out[x]]}else{if(have[x]) update(in[x],-1);else update(in[x],1);have[x] ^= 1; //have[x] = !have[x];}}}return 0;
}
2.树链修改,单点查询
将一条树链x,y上的所有点的权值加v。这个问题可以等价为:
1).x到根节点的链上所有节点权值加v。
2).y到根节点的链上所有节点权值加v。
3).lca(x,y)到根节点的链上所有节点权值和减v。
4).fa(lca(x,y))到根节点的链上所有节点权值和减v。
上面四个操作可以归结为:节点x到根节点链上所有节点的权值加减v。修改节点x权值,当且仅当y是x的祖先节点时,x对y的值有贡献。
所以节点y的权值可以转化为节点y的子树节点贡献和。从贡献和的角度想:这就是点修改,区间和查询问题。
修改树链x,y等价于add(l[x],v),add(l[y],v),add(l[lca(x,y)],-v),add(l[fa(lca(x,y))],-v)。
查询:get_sum(r[x])-get_sum(l[x]-1)
用树状数组或线段树即可。
3.树链修改,子树和查询
树链修改部分同上一问题。下面考虑子树和查询问题:前一问是从贡献的角度想,子树和同理。
对于节点y其到根节点的权值和,考虑其子节点x的贡献:w[x]*(deep[x]-deep[y]+1) = w[x]*(deep[x]+1)-w[x]*deep[y]
所以节点y的子树和为:
ps:公式中的v[i]为手误,应为w[i]。
所以用两个树状数组或线段树即可:
第一个维护∑w[i]*(deep[i]+1):支持操作单点修改,区间和查询。(这也就是问题2)
第二个维护∑ w[i]:支持操作单点修改,区间查询。(这其实也是问题2)
4.单点更新,树链和查询
树链和查询与树链修改类似,树链和(x,y)等于下面四个部分和相加:
1).x到根节点的链上所有节点权值加。
2).y到根节点的链上所有节点权值加。
3).lca(x,y)到根节点的链上所有节点权值和的-1倍。
4).fa(lca(x,y))到根节点的链上所有节点权值和的-1倍。
所以问题转化为:查询点x到根节点的链上的所有节点权值和。
修改节点x权值,当且仅当y是x的子孙节点时,x对y的值有贡献。
差分前缀和,y的权值等于dfs中[1,l[y]]的区间和。
单点修改:add(l[x],v),add(r[x]+1,-v);
5.子树修改,单点查询
修改节点x的子树权值,在dfs序上就是区间修改,单点权值查询就是单点查询。
区间修改,单点查询问题:树状数组或线段树即可;
6.子树修改,子树和查询
题目等价与区间修改,区间查询问题。用树状数组或线段树即可。
7.子树修改,树链查询
树链查询同上,等价为根节点到y节点的链上所有节点和问题。
修改节点x的子树权值,当且仅当y是x的子孙节点时(或y等于x),x对y的值有贡献。
x对根节点到y节点的链上所有节点和的贡献为:w[x]*(deep[y]-deep[x]+1)=w[x]*deep[y]-w[x]*(1-deep[x])
同问题三,用两个树状数组或线段树即可。
参考文章:树 DFS序 详解[完全版]
初学dfs序
DFS序详解
dfs序的七个基本问题来源如下
作者:weeping
出处:www.cnblogs.com/weeping/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
DFS序——树链剖分前驱知识相关推荐
- 简单dfs序 + 树链剖分
树链剖分 DFS序 先来讲一讲DFS序是什么东西,直接上图,方便理解. 估计巨巨们应该知道了DFS序的两个重要的东西,in,outin,outin,out数组. ininin数组就是这个点进入DFS的 ...
- HDU - 3804 Query on a tree(树链剖分+线段树+离线处理)
题目链接:点击查看 题目大意:给出一棵树,每条边上都有一个权值,给出m个查询:a,b:问从点1到点a的唯一路径上,在边权小于等于b的边中选出边权最大的值输出,若没有符合条件的边则输出-1: 题目分析: ...
- 树链剖分(入门学习)
学习博客:https://www.cnblogs.com/ivanovcraft/p/9019090.html 先来回顾两个问题: 1,将树从x到y结点最短路径上所有节点的值都加上z 这也是个模板题了 ...
- 树链剖分 or 根号分治 + dfs序 + 树状数组 ---- CF1254 D. Tree Queries
题目链接 题目大意: 问题转化: 很容易发现:假设修改的节点是vvv. 1.vvv的子树sonvson_vsonv直接加上(n−size[sonv])n×d\frac{(n-size[son_v]) ...
- bzoj3252攻略(线段树+dfs序)或者(树链剖分+dfs)
3252: 攻略 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 1341 Solved: 642 [Submit][Status][Discuss] ...
- BZOJ 3083: 遥远的国度(树链剖分+DFS序)
可以很显而易见的看出,修改就是树链剖分,而询问就是在dfs出的线段树里查询最小值,但由于这道题会修改根节点,所以在查询的时候需判断x是否为root的祖先,如果不是就直接做,是的话应该查询从1-st[y ...
- 蒟蒻浅谈树链剖分之一——两个dfs操作
树链剖分,顾名思义就是将树形的结构剖分成链,我们以此便于在链上操作 首先我们需要明白在树链剖分中的一些概念 重儿子:某节点所有儿子中子树最多的儿子 重链:有重儿子构成的链 dfs序:按重儿子优先遍历时 ...
- 【模板】树链剖分 P3384
题目链接 //部分转自:https://www.luogu.org/problemnew/solution/P3384 初学树链剖分,感觉这个模板题还是容易理解的,但是实在是码量很大的. 知识点: 重 ...
- 树分治树链剖分相关题目讨论
预备知识 树分治,树链剖分 poj1741 •一棵有n个节点的树,节点之间的边有长度.方方方想知道,有多少个点对距离不超过m 题解 点分治模板题.详见我早上写的http://www.cnblogs.c ...
最新文章
- APUE读书笔记-18终端输入输出-14总结
- java 子类重写父类的方法的注意事项
- php 运行外部程序_php 中运行外部程序的一个潜在风险
- matlab并联负荷模块,Matlab-SimPowerSystems-Elements模块使用说明
- 如何生成HDF5文件
- 成为指标的“绝地武士”:tableau创建指标的 10 个技巧和窍门
- assertequal用法python_assertEqual和assertIs之间有什么区别(assertIs是在Python 2.7中引入的)?...
- 多命令顺序执行,单引号,双引号,反引号,转义符
- 适用于具有Couchbase和WildFly的多容器和多主机应用程序的Docker Machine,Swarm和Compose...
- 超全干货:数据可视化的设计总结,工具,技术要点统统都有
- Linux下企业级分区方案
- 怎样利用通达信公式选股?
- 2017美赛C题论文学习笔记
- win10应用商店打不开及打开之后下载失败问题解决方法
- c语言数组的斐波那契数列
- exploit计算机术语,exploit
- 帆船运行员训练方法研究
- 用幂律分布研究工资收入
- java内存可视化_JVM系列(六)、可视化工具介绍
- 怎么选择合适的PLM系统