DFS序两道模板题+个人理解 10.17倒计时

DFS序可用于解决一些树上单点修改与查询问题

这样就可以在线处理,无需每一次修改跑一遍最短路或是其他带n的算法

在树中,遇到单点修改和查询的问题,可以考虑dfs+线段树or树状数组维护。

Dfs抽象来说就是将一棵树由树状结构转化为线性结构,一个点到它的所有子树节点占据连续的位置。利用一遍的dfs记录每个点的起始位置(in数组)和结束位置(out数组),点的先后并不取决于存储方式,不变的是每个节点所占据的长度即out[x]-in[x]。

 

今天做的两道题,套路可以说相对固定,但是会有很多细节问题导致WA或RE,需亲自下手:

HDU5692 Snacks

Problem Description

百度科技园内有n个零食机,零食机之间通过n−1条路相互连通。每个零食机都有一个值v,表示为小度熊提供零食的价值。

由于零食被频繁的消耗和补充,零食机的价值v会时常发生变化。小度熊只能从编号为0的零食机出发,并且每个零食机至多经过一次。另外,小度熊会对某个零食机的零食有所偏爱,要求路线上必须有那个零食机。

为小度熊规划一个路线,使得路线上的价值总和最大。

Input

输入数据第一行是一个整数T(T≤10),表示有T组测试数据。

对于每组数据,包含两个整数n,m(1≤n,m≤100000),表示有n个零食机,m次操作。

接下来n−1行,每行两个整数x和y(0≤x,y<n),表示编号为x的零食机与编号为y的零食机相连。

接下来一行由n个数组成,表示从编号为0到编号为n−1的零食机的初始价值v(|v|<100000)。

接下来m行,有两种操作:0 x y,表示编号为x的零食机的价值变为y;1 x,表示询问从编号为0的零食机出发,必须经过编号为x零食机的路线中,价值总和的最大值。

本题可能栈溢出,辛苦同学们提交语言选择c++,并在代码的第一行加上:

`#pragma comment(linker, "/STACK:1024000000,1024000000") `

Output

对于每组数据,首先输出一行”Case #?:”,在问号处应填入当前数据的组数,组数从1开始计算。

对于每次询问,输出从编号为0的零食机出发,必须经过编号为x零食机的路线中,价值总和的最大值。

Sample Input

1

6 5

0 1

1 2

0 3

3 4

5 3

7 -5 100 20 -5 -7

1 1

1 3

0 2 -1

1 1

1 5

Sample Output

Case #1:

102

27

2

20

题目概括

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

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

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

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

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

题解

线段树。

我们设:

w[x]为节点x的权值

Dis[x]为从根节点到达当前节点的权值

那么,一开始,dis[x]可以通过一遍dfs全部求出来。

如何处理更改和询问呢?那么我们从询问入手。

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

那么我们想到了什么?因为在树的dfs序中,同一个子树节点的所对应的序号一定是整个dfs序中的连续的一段!

那么我们就可以把询问转化成“求区间最大值”的问题了。然而同理思考,那么修改其实就是修改整个子树的最大值!对于0 x y, 其实就是子树x的所有节点都增加y-w[x]!那么最大值也增加y-w[x]。

问题就变成了一个询问区间最大值和区间修改(同增或者同减)的问题了。

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

 

给出一组查错数据:

/*

4

9 5

1 0

1 2

2 3

4 1

5 3

6 5

7 5

7 8

23182 80368 7060 -50161 81799 8841 90480 3016 -4312

1 0

0 8 -438497

0 4 -188568

1 4

0 6 -176104

 

9 4

0 1

0 2

3 0

3 4

3 5

6 1

7 6

0 8

61060 -24449 -72783 -77927 -33421 80849 8262 -24364 90327

0 2 116045

0 5 404857

1 2

1 5

 

7 8

1 0

0 2

2 3

4 2

2 5

6 1

16405 -88702 4022 40275 80451 68322 78648

0 3 -39689

1 3

0 3 100112

0 2 109684

1 6

1 2

0 3 -345744

0 3 144465

 

7 2

0 1

2 0

0 3

1 4

5 0

4 6

-31521 32600 -32746 -67252 40896 94763 99624

0 5 372906

0 5 -118828

 

ans:

 

Case #1:

185349

-85018

Case #2:

177105

387990

Case #3:

-19262

6351

226201

Case #4:

*/

POJ3321 Apple Tree

 

题意概括

有一颗01树,以结点1为树根,一开始所有的结点权值都是1,有两种操作:

  1.改变其中一个结点的权值(0变1,1变0)

  2.询问子树X的节点权值和。

 

输入描述

一组数据。

先是一个数n,表示有n个节点。

接下去n-1行,每行表示一条边。

然后一个数m,表示有m个操作。

然后m行,每行一个字母一个数x,如果字母是Q,则是询问;否则是修改。

 

输出描述

每一个询问一行,表示答案。

 

题解

题目变成了单点修改和区间sum询问的问题。

单点修改,不用线段树,树状数组就可以了。

 

第一题:

#pragma comment(linker, "/STACK:1024000000,1024000000")

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <cmath>

#include <map>

#include <cstring>

#include <string>

#include <set>

#include <vector>

#define LL long long

using namespace std;

const int maxn=1e5+5;

const LL INF=1e18;

int n,m,k;

LL w[maxn],dis[maxn];

int time,in[maxn],out[maxn],head[maxn];

struct TreeNode{

         LL v,add;

         int l,r;

}tr[maxn<<2];

struct Edge{

         int u,v,next;

}e[maxn];

void addedge(int u,int v){

         e[++k]=(Edge){u,v,head[u]};

         head[u]=k;

         e[++k]=(Edge){v,u,head[v]};

         head[v]=k;

}

void pushup(int id){

         tr[id].v=max(tr[id<<1].v,tr[id<<1|1].v);

}

void pushdown(int id){

         if(tr[id].add){

                   LL temp=tr[id].add;

                   tr[id<<1].v+=temp;

                   tr[id<<1].add+=temp;

                   tr[id<<1|1].v+=temp;

                   tr[id<<1|1].add+=temp;

                   tr[id].add=0;

         }

}

void build(int l,int r,int id){

         tr[id].l=l; tr[id].r=r; tr[id].add=0;

         if(l==r){

                   tr[id].v=dis[l];

                   return;

         }

         int m=(l+r)>>1;

         build(l,m,id<<1);

         build(m+1,r,id<<1|1);

         pushup(id);

}

 

void update(int L,int R,LL d,int id){

         if(tr[id].l>R || tr[id].r<L) return;

         if(tr[id].l>=L && tr[id].r<=R){

                   tr[id].v+=d;

                   tr[id].add+=d;

                   return;

         }

         pushdown(id);

         int m=(tr[id].l+tr[id].r)>>1;

        update(L,R,d,id<<1);

         update(L,R,d,id<<1|1);

         pushup(id);

}

LL query(int L,int R,int id){

         if(tr[id].l>R || tr[id].r<L) return -INF;

         if(tr[id].l>=L && tr[id].r<=R){

                   return tr[id].v;

         }

         pushdown(id);

         int m=(tr[id].l+tr[id].r)>>1;

         return max(query(L,R,id<<1),query(L,R,id<<1|1));

         pushup(id);

}

void dfs(int rt,int pre){

         in[rt]=++time;

         dis[in[rt]]=dis[in[pre]]+w[rt];

         for(int i=head[rt];i!=-1;i=e[i].next){

                   if(e[i].v!=pre) dfs(e[i].v,rt);

         }                

         out[rt]=time;

}

void debug(){

         for(int i=0;i<n;i++){

                   cout<<"in= "<<in[i]<<" out="<<out[i]<<endl;

         }

}

 

int main(){

         int op,_,a,b; LL z;cin>>_;

         int t=0;

         while(_--){

                   scanf("%d%d",&n,&m); 

                   k=0;

                   memset(tr,0,sizeof tr);

                   memset(head,-1,sizeof head);

                   for(int i=0;i<n-1;i++){

                            scanf("%d%d",&a,&b);

                            a++; b++;

                            addedge(a,b);

                   }

                   for(int i=1;i<=n;i++) scanf("%lld",&w[i]);

                   in[0]=out[0]=time=0;

                   dfs(1,0); //debug();

                   build(1,n,1);

                   printf("Case #%d:\n",++t);

                   while(m--){

                            scanf("%d",&op);

                            if(op==0){

                                     scanf("%d%lld",&a,&z);

                                     a++;

                                     update(in[a],out[a],z-w[a],1);

                                     w[a]=z;

                            }

                            else{

                                     scanf("%d",&a);

                                     a++;

                                     printf("%lld\n",query(in[a],out[a],1));

                            }                          

                   }

                  

        

         }

         return 0;

}

第二题:

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <cmath>

#include <map>

#include <cstring>

#include <string>

#include <set>

#include <vector>

#define LL long long

using namespace std;

const int maxn=100005;

int n,m,k,time;

bool state[maxn];

int h[maxn],in[maxn],out[maxn],head[maxn],sum[maxn];

struct edge{

         int u,v,next;

}e[maxn];

void dfs(int rt,int pre){

         in[rt]=++time;

         for(int i=head[rt];i;i=e[i].next){

                   if(e[i].v!=pre) dfs(e[i].v,rt);

         }

        

         out[rt]=time;

}

 

int lowbit(int i){

         return i&(-i);

}

void update(int i,int x){

         while(i<maxn){

                   h[i]+=x;    

                   i=i+lowbit(i);

         }

}

int query(int i){

         int res=0;

         while(i>0){

                   res+=h[i];

                   i=i-lowbit(i);

         }

         return res;

}

void addedge(int u,int v){

         e[++k]=(edge){u,v,head[u]};

         head[u]=k;

         e[++k]=(edge){v,u,head[v]};

         head[v]=k;       

}

 

int main(){

         int a,b;

         char op;

         cin>>n;

         k=0;

         memset(head,0,sizeof head);

         for(int i=0;i<n-1;i++){

                   scanf("%d%d",&a,&b);

                   addedge(a,b);

         }

         h[0]=0;

         for(int i=1;i<=n;i++) {

                   h[i]=lowbit(i);

                   state[i]=1;

         }

         in[0]=out[0]=time=0;

         dfs(1,0);

         cin>>m;

         while(m--){

                   getchar();

                   scanf("%c %d",&op,&a);

                   if(op=='C'){

                            if(state[a]){state[a]=0;update(in[a],-1);}

                           else{state[a]=1;update(in[a],1);}

                   }

                   else{

                            printf("%d\n",query(out[a])-query(in[a]-1));

                   }

        

         }

         return 0;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值