目录

定义: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序——树链剖分前驱知识相关推荐

  1. 简单dfs序 + 树链剖分

    树链剖分 DFS序 先来讲一讲DFS序是什么东西,直接上图,方便理解. 估计巨巨们应该知道了DFS序的两个重要的东西,in,outin,outin,out数组. ininin数组就是这个点进入DFS的 ...

  2. HDU - 3804 Query on a tree(树链剖分+线段树+离线处理)

    题目链接:点击查看 题目大意:给出一棵树,每条边上都有一个权值,给出m个查询:a,b:问从点1到点a的唯一路径上,在边权小于等于b的边中选出边权最大的值输出,若没有符合条件的边则输出-1: 题目分析: ...

  3. 树链剖分(入门学习)

    学习博客:https://www.cnblogs.com/ivanovcraft/p/9019090.html 先来回顾两个问题: 1,将树从x到y结点最短路径上所有节点的值都加上z 这也是个模板题了 ...

  4. 树链剖分 or 根号分治 + dfs序 + 树状数组 ---- CF1254 D. Tree Queries

    题目链接 题目大意: 问题转化: 很容易发现:假设修改的节点是vvv. 1.vvv的子树sonvson_vsonv​直接加上(n−size[sonv])n×d\frac{(n-size[son_v]) ...

  5. bzoj3252攻略(线段树+dfs序)或者(树链剖分+dfs)

    3252: 攻略 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 1341 Solved: 642 [Submit][Status][Discuss] ...

  6. BZOJ 3083: 遥远的国度(树链剖分+DFS序)

    可以很显而易见的看出,修改就是树链剖分,而询问就是在dfs出的线段树里查询最小值,但由于这道题会修改根节点,所以在查询的时候需判断x是否为root的祖先,如果不是就直接做,是的话应该查询从1-st[y ...

  7. 蒟蒻浅谈树链剖分之一——两个dfs操作

    树链剖分,顾名思义就是将树形的结构剖分成链,我们以此便于在链上操作 首先我们需要明白在树链剖分中的一些概念 重儿子:某节点所有儿子中子树最多的儿子 重链:有重儿子构成的链 dfs序:按重儿子优先遍历时 ...

  8. 【模板】树链剖分 P3384

    题目链接 //部分转自:https://www.luogu.org/problemnew/solution/P3384 初学树链剖分,感觉这个模板题还是容易理解的,但是实在是码量很大的. 知识点: 重 ...

  9. 树分治树链剖分相关题目讨论

    预备知识 树分治,树链剖分 poj1741 •一棵有n个节点的树,节点之间的边有长度.方方方想知道,有多少个点对距离不超过m 题解 点分治模板题.详见我早上写的http://www.cnblogs.c ...

最新文章

  1. APUE读书笔记-18终端输入输出-14总结
  2. java 子类重写父类的方法的注意事项
  3. php 运行外部程序_php 中运行外部程序的一个潜在风险
  4. matlab并联负荷模块,Matlab-SimPowerSystems-Elements模块使用说明
  5. 如何生成HDF5文件
  6. 成为指标的“绝地武士”:tableau创建指标的 10 个技巧和窍门
  7. assertequal用法python_assertEqual和assertIs之间有什么区别(assertIs是在Python 2.7中引入的)?...
  8. 多命令顺序执行,单引号,双引号,反引号,转义符
  9. 适用于具有Couchbase和WildFly的多容器和多主机应用程序的Docker Machine,Swarm和Compose...
  10. 超全干货:数据可视化的设计总结,工具,技术要点统统都有
  11. Linux下企业级分区方案
  12. 怎样利用通达信公式选股?
  13. 2017美赛C题论文学习笔记
  14. win10应用商店打不开及打开之后下载失败问题解决方法
  15. c语言数组的斐波那契数列
  16. exploit计算机术语,exploit
  17. 帆船运行员训练方法研究
  18. 用幂律分布研究工资收入
  19. java内存可视化_JVM系列(六)、可视化工具介绍
  20. 怎么选择合适的PLM系统

热门文章

  1. 小程序开发需要什么步骤?步骤教程分享
  2. maya之坐标轴与模型显示状态
  3. 2020找工作更难了?做好这4方面,找到高薪好工作
  4. 全球屏占比最高!华为MatePad Pro震撼发布
  5. 企业微信如何给客户打标签?
  6. Linux-CentOS上的服务搭建
  7. 判断两个矩形是否重叠
  8. 关于地理数据收集与处理的基本工具推荐(2)---10m精度的全球土地覆盖数据下载
  9. 手机电脑都能用,将照片转成PDF的免费方法
  10. 如何修改 Kubernetes 节点 IP 地址?