浅谈DFS序

广义的dfs序是指在树上dfs时各种巧妙记录留下的一个线性序列 从而把树上问题转化为序列问题便于解决

由于问题的类型各异 dfs序的种类也有很多 比如  入栈序入栈出栈序、 出栈序、 树链剖分序等。

先贴个代码镇住场面

int id,a[N],ord[N],fa[N],sum[N],deep[N];
void dfs(int x)
 {
  //a[++id]=x; // 这行代码表示把当前点加入序列中 根据问题实际操作
  //ord[x]=id; // ord[i]表示i号节点出现在dfs序的位置 根据问题可能用多个数组分情况存
 
  sum[x]=1;    //初始化子树大小
  for(int i=last[x];i;i=nxt[i])
   {int y=ver[i];
    if(y==fa[x]) return;
    fa[y]=x; deep[y]=deep[x]+1;
    dfs(y);
    sum[x]+=sum[y];
    //a[++id]=x; //ord[x]=id;
   }
 //a[++id]=x; //ord[x]=id;
 }

是不是顿时觉得dfs序是个很好学的东西?

dfs序的重要性质 一个点和他所有的子节点会被存储在连续的区间之中。

证明:因为是dfs,所以显然,命题得证;

第一部分  dfs序

 

dfs序能干啥?

 

1.dfs序+数据结构

dfs序+数据结构强无敌之一  POJ3321 Apple Tree 

给定一棵n个点的有根苹果树,每个点上最多只有一个苹果,现在有Q次操作:

(1).修改一个点上的苹果个数。

(2).求某个子树内有几个苹果。

n,Q<=10^5.

 

题解:裸题  单点修改区间查询 用树状数组维护就好。

 

dfs序+数据结构强无敌之二 HDU5692 Snacks 

给定一棵n个点的有根树,每个点有一个点权。根节点为0,节点标号为0~n-1。

定义最大路径为:从根出发走到某个点,点权和最大的路径。

现在有Q次操作,每种是以下两种之一:

(1).将点x的点权变成v。

(2).求经过某一个点的最大路径的点权和。

 

题解:

设w[x]为节点x的权值

dis[x]为从根节点到达当前节点的权值,可以通过一遍dfs全部求出来。

题目询问的是经过一个点的最大路径的点权和,那么其实就是到达这个节点或者其子孙节点的最大dis[x], 其实就是查询整个子树的max(dis[x])。

修改其实就是修改整个子树的最大值!对于0 x y, 其实就是子树x的所有节点都增加y-w[x]!那么最大值也增加y-w[x]。

问题就变成了一个询问区间最大值和区间修改的问题了。

于是,我们就可以用线段树来维护。

 

https://blog.csdn.net/qq_39670434/article/details/78425125

dfs序+数据结构强无敌之三 

对节点X到Y的最短路上所有点权都加一个数W, 查询某个点的权值 

 

题解:
树上差分 路径修改转化为单点修改  单点查询转化为子树和查询 之后显然

 

dfs序+数据结构强无敌之四

对节点X到Y的最短路上所有点权都加一个数W, 查询某个点子树的权值之和 


题解 : 

树上差分 路径修改转化为4个点修改
当修改某个节点A, 查询另一节点B时 
只有A在B的子树内, 子树权值和会增加W×(depth[A]−depth[B]+1)=W×(depth[A]+1)−W×depth[B]
同样是用树状数组来查询子树, 修改 和 查询方法都要新增一个数组 
第一个数组保存 W×(depth[A]+1) 第二个数组保存 W 
每次查询结果为Sum(ed[B])−Sum(st[B]−1)−(Sum1(ed[B])−Sum1(st[B]−1))×depth[B]

 

dfs序+数据结构强无敌之五

对某个点X权值加上一个数W, 查询X到Y路径上所有点权之和 


题解:
求X到Y路径上所有的点权之和, 和前面X到Y路径上所有点权加一个数相似 
这个问题转化为 
X到根节点的和 + Y到根节点的和 −LCA(x,y)到根节点的和 −fa(LCA(x,y))到根节点的和 
于是只要支持单点修改, 区间查询即可 

为了保证在该节点子树内的能加到这个W, 不在该点子树内的无影响
我们要dfs求出入栈出栈序   在该点开始出现位置加W, 在结束位置减W  

2.dfs序+DP

 

dfs序+DP强无敌之一

O(nm)构造树背包

那么有没有可能在O(nm)的时间复杂度内构造树背包呢?

有可能!

我们可以求出树的后序遍历,将树上问题转化为序列问题。

f[i][0~m]代表的不再是“以i为根的子树的背包”,而是“后序遍历序列的前i个节点上的物品所构成的背包”。

对于序列上第i个点,令其所代表的节点为k。节点k上的物品只有选和不选之分,且只有选了它才可能选它的子树。

因为k所在的子树在后序遍历序列上是连续的一段,所以在更新f[i][0~m]之前先将f[i-size[k]][0~m]的值copy过来,这表示着不选k点的答案;然后在f[i-1][0~m]这个背包上添加k点上的物品来更新f[i][0~m],表示选择k点的答案。

for(j=0;j<=m;++j) f[i][j]=f[i-size[k]][j];

for(j=w[k];j<=m;++j) f[i][j]=max(f[i][j],f[i-1][j-w[k]]+v[k]);

这就是计算大基佬zP1nG退役后的遗作  现在让我这个蒟蒻借鉴一番

 

 

第二部分   欧拉序及其重要性质

https://www.cnblogs.com/pealicx/p/6859901.html

欧拉序貌似有两个版本

1.就是从根结点出发,按dfs的顺序在绕回原点所经过所有点的顺序

dfs到加进,dfs回加进,总共加度数遍;

void dfs(int x,int dep)
 {que[++tot]=x;  
  first[x]=tot;
  deep[tot]=dep;
  for(int i=last[x];i;i=nxt[i])
    {dfs(v[i],dep+1);
     que[++tot]=x;
     deep[tot]=dep;
    }
 que[++tot]=x;
 deep[tot]=dep;
 }

2.入栈出栈序

欧拉序求lca

 两个点的  对应第一次在欧拉序中出现的位置 所夹的在deep数组闭区间的最小值 对应的 点

然后就可以用ST表维护 ST回答询问为O(1)

欧拉序lca适合询问数大的情况  

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
short int n,last[1005],nxt[1005],v[1005],num,l2[2005];   
short int que[2005],tot,first[1005],deep[2005],f[2005][12];
int fang[12]={1,2,4,8,16,32,64,128,256,512,1024,2048},ans[1005];
bool fa[1005];
void dfs(int x,int dep)
 {que[++tot]=x;  
  first[x]=tot;
  deep[tot]=dep;
  for(int i=last[x];i;i=nxt[i])
    {dfs(v[i],dep+1);
     que[++tot]=x;
     deep[tot]=dep;
    }
 }
int main()
 {  int p,q,l,r,m;
    scanf("%d",&n);
    for(int i=1;i<n;i++) 
	   {scanf("%d%d",&p,&q);   
	    nxt[++num]=last[p];
        last[p]=num;
        v[num]=q;   fa[q]=1;
       }
	
	for(int i=1;i<=n;i++) if(!fa[i]) dfs(i,1);
	
    for(int i=1;i<=tot;i++) f[i][0]=i;
    
     int k=1; l2[1]=0;
    for(int i=2;i<=tot;i++)
     {l2[i]=k;if(fang[k+1]==i) k++;}
     
    for(int j=1;fang[j]<=tot;j++)
     for(int i=1;i+fang[j]-1<=tot;i++)
       if(deep[f[i][j-1]]<deep[f[i+fang[j-1]][j-1]]) f[i][j]=f[i][j-1];
       else                                          f[i][j]=f[i+fang[j-1]][j-1];
    
     scanf("%d",&m);
     for(int i=1;i<=m;i++)
     {scanf("%d%d",&p,&q); 
      l=first[p]; r=first[q]; if(l>r) swap(l,r);
	  p=l2[r-l+1];
      if(deep[f[l][p]]<deep[f[r-fang[p]+1][p]]) ans[que[f[l][p]]]++;
      else                                      ans[que[f[r-fang[p]+1][p]]]++;
     }
    for(int i=1;i<=n;i++)
     if(ans[i]>0) printf("%d:%d\n",i,ans[i]);
 return 0;
 }

根据求欧拉序lca的算法 

我们可以得出一个结论

对于多个点求lca 相当于入栈序最小的点和入栈序最大的点求lca

 

欧拉游览树 动态维护欧拉序

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于Django+python编写开发的毕业生就业管理系统支持学生教师角色+db数据库(毕业设计新项目).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 基于Django+python编写开发的毕业生就业管理系统支持学生教师角色+db数据库(毕业设计新项目).zip基于Django+python编写开发的毕业生就业管理系统支持学生教师角色+db数据库(毕业设计新项目).zip基于Django+python编写开发的毕业生就业管理系统支持学生教师角色+db数据库(毕业设计新项目).zip基于Django+python编写开发的毕业生就业管理系统支持学生教师角色+db数据库(毕业设计新项目).zip基于Django+python编写开发的毕业生就业管理系统支持学生教师角色+db数据库(毕业设计新项目).zip基于Django+python编写开发的毕业生就业管理系统支持学生教师角色+db数据库(毕业设计新项目).zip基于Django+python编写开发的毕业生就业管理系统支持学生教师角色+db数据库(毕业设计新项目).zip基于Django+python编写开发的毕业生就业管理系统支持学生教师角色+db数据库(毕业设计新项目).zip基于Django+python编写开发的毕业生就业管理系统支持学生教师角色+db数据库(毕业设计新项目).zip
毕设新项目基于python3.7+django+sqlite开发的学生就业管理系统源码+使用说明(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 学生就业管理系统(前端) ## 项目开发环境 - IDE: vscode - node版本: v12.14.1 - npm版本: 6.13.4 - vue版本: @vue/cli 4.1.2 - 操作系统: UOS 20 ## 1.进入项目目录安装依赖 ``` npm install ``` ## 2.命令行执行进入UI界面进行项目管理 ``` vue ui ``` ## 3.编译发布包(请注意编译后存储路径) #### PS:需要将编译后的包复制到后端项目的根目录下并命名为'static' 学生就业管理系统(后端) ## 1.项目开发环境 - IDE: vscode - Django版本: 3.0.3 - Python版本: python3.7.3 - 数据库 : sqlite3(测试专用) - 操作系统 : UOS 20 ## 2.csdn下载本项目并生成/安装依赖 ``` pip freeze > requirements.txt pip install -r requirements.txt ``` ## 3.项目MySQL数据库链接错误 [点击查看解决方法](https://www.cnblogs.com/izbw/p/11279237.html)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值