先介绍下DFS序
树的dfs序就是用来维护一系列树上的问题的,这类问题主要是解决一棵树上的所有后代结点信息的更改和祖先结点有关,主要先通过dfs来记录一个树的每一个顶点的出入时间戳,来控制它子树上的所有结点的状态的更新。
用in数组记录每个结点入栈的时间,out数组记录出栈时间(如果一个结点出栈之前没有结点出栈,那么出栈时间为上一个结点(可能是它自己)的入栈时间;否则出栈时间就等于上一个点的出栈时间)
由图可知,每一个点的in和out(在图中是start和end)就是该结点所在的区间,也就构成了一棵线段树,也就可以用
线段树来操作了。
dfs序代码:
//链式前向星存图
void dfs(int x,int fa)
{
q[++id]=x;
in[x]=id;
for(int i=head[x];~i;i=eg[i].next)
{
if(eg[i].to!=fa)
{
dfs(eg[i].to,x);
}
}
out[x]=id;
}
线段树维护DFS序
这里列出我做过的两种维护DFS序的题
第一种——更改某个结点只影响其父节点(POJ 3321)
题意:
给一棵n个节点的树,每个节点开始有一个苹果,m次操作
1.将某个结点的苹果数异或 1
2.查询一棵子树内的苹果数
思路:因为更改某个结点不影响其子结点,而影响其父结点,所以只需pushUp操作即可,update的时候也是到l==r(找到要更改的结点)对其更改,query的时候就是找in[a]到out[a]这段区间的和了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#include<iomanip>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const int maxn=200005;
const double eps=1e-8;
const double PI = acos(-1.0);
struct node
{
int to,next;
}eg[maxn];
int head[maxn],tot=0,sum[maxn<<2];
void init()
{
memset(head,-1,sizeof(head));
tot=0;
}
void addedge(int u,int v)
{
eg[tot].to=v;
eg[tot].next=head[u];
head[u]=tot++;
}
int in[maxn],out[maxn],id=0,q[maxn];
void dfs(int x,int fa)
{
q[++id]=x;
in[x]=id;
for(int i=head[x];~i;i=eg[i].next)
{
if(eg[i].to!=fa)
{
dfs(eg[i].to,x);
}
}
out[x]=id;
}
void pushUp(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
if(l==r)
{
sum[rt]=1;
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushUp(rt);
}
void update(int L,int l,int r,int rt)
{
if(l==r)
{
sum[rt]^=1;
return ;
}
int m=(l+r)>>1;
if(m>=L) update(L,l,m,rt<<1);
else update(L,m+1,r,rt<<1|1);
pushUp(rt);
}
int query(int L,int R,int l,int r,int rt)
{
if(l>=L&&r<=R)
{
return sum[rt];
}
int m=(l+r)>>1;
int ans=0;
if(m>=L)
ans+=query(L,R,l,m,rt<<1);
if(m<R)
ans+=query(L,R,m+1,r,rt<<1|1);
return ans;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int n;
while(cin>>n)
{
id=0;
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
init();
for(int i=0;i<n-1;i++)
{
int a,b;
cin>>a>>b;
addedge(a,b);
addedge(b,a);
}
dfs(1,0);
int m;
cin>>m;
build(1,n,1);
while(m--)
{
char c;
int a;
cin>>c>>a;
if(c=='C')
{
update(in[a],1,n,1);
}
else
{
cout<<query(in[a],out[a],1,n,1)<<endl;
}
}
}
return 0;
}
第二种——更改某个结点只影响其子结点(HDU - 3974)
题意:
有一家公司有N个员工(从1到N),公司里每个员工都有一个直接的老板(除了整个公司的领导)。如果你是某人的直接老板,那个人就是你的下属,他的所有下属也都是你的下属。如果你是没有人的老板,那么你就没有下属,没有直接老板的员工就是整个公司的领导,也就是说N个员工构成了一棵树。公司通常把一些任务分配给一些员工来完成,当一项任务分配给某个人时,他/她会把它分配给他/她的所有下属,换句话说,这个人和他/她的所有下属在同一时间接受了一项任务。此外,每当员工收到一个任务,他/她将停止当前任务(如果他/她有),并开始新的任务。在公司将某些任务分配给某个员工后,编写一个程序来帮助找出某个员工当前的任务。
思路:因为更改某个点不影响其父节点,所以只用pushDown操作,把根节点信息下推,所以update里要传两个参数L和R,分别是in[x],out[x],定位到这个区间,然后更改,再下推更新其子结点。query的时候因为是要找某个点的任务是什么,所以直接找in[x]就行了,即l==r的时候返回sum[rt]。我这里是邻接表存的边,感觉比前向星更简洁,而且前向星交上去RE我也不知道怎么回事呜呜呜……
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const int maxn=50005;
const int mod=1e9+7;
const double eps=1e-8;
const double PI = acos(-1.0);
#define lowbit(x) (x&(-x))
int sum[maxn<<2],id;
int in[maxn],out[maxn],vis[maxn],n;
vector<int> g[maxn];
void init()
{
for(int i=0;i<maxn;i++)
g[i].clear();
memset(vis,0,sizeof(vis));
// memset(sum,-1,sizeof(sum));
id=0;
}
void addedge(int u,int v)
{
g[u].push_back(v);
}
void dfs(ll x)
{
in[x]=++id;
for(int i=0;i<g[x].size();i++)
{
dfs(g[x][i]);
}
out[x]=id;
}
void pushDown(int rt)
{
if(sum[rt]!=-1)
{
sum[rt<<1]=sum[rt];
sum[rt<<1|1]=sum[rt];
sum[rt]=-1;
}
}
void build(int l,int r,int rt)
{
sum[rt]=-1;//开始所有节点赋为-1
if(l==r)
{
return ;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
}
void update(int L,int R,int C,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
sum[rt]=C;
return ;
}
pushDown(rt);
int m=(l+r)>>1;
if(m>=L)
{
update(L,R,C,l,m,rt<<1);
}
if(R>m)
update(L,R,C,m+1,r,rt<<1|1);
}
int query(int L,int l,int r,int rt)
{
if(l==r)
{
return sum[rt];
}
pushDown(rt);
int m=(l+r)>>1;
if(m>=L)
query(L,l,m,rt<<1);
else
query(L,m+1,r,rt<<1|1);
}
int main()
{
std::ios::sync_with_stdio(false);
int t,cas=0;
cin>>t;
while(t--)
{
init();
cin>>n;
for(int i=0;i<n-1;i++)
{
int u,v;
cin>>u>>v;
addedge(v,u);
vis[u]=1;
}
cout<<"Case #"<<++cas<<":"<<endl;
for(int i=1;i<=n;i++)
{
if(!vis[i])
dfs(i);
}
/* for(int i=1;i<=n;i++)
{
cout<<in[i]<<" "<<out[i]<<endl;
}*/
build(1,n,1);
int m;
cin>>m;
while(m--)
{
char c;
int x,y;
cin>>c>>x;
if(c=='T')
{
cin>>y;
// cout<<in[x]<<" "<<out[x]<<endl;
update(in[x],out[x],y,1,n,1);
}
else
{
cout<<query(in[x],1,n,1)<<endl;
}
}
}
return 0;
}