dfs序是用来处理子树一类问题的,可以把子树问题转化为区间问题,以便借助线段树或树状数组处理。根据dfs的顺序来给节点编号,在进入这个节点时更新in数组,出去的时候更新out数组,这样以i为根的子树的操作就可以变成区间[in[i]…out[i]]的操作了。
int in[100005],out[100005];
int tot = 1;
void dfs(int x,int fa)
{
in[x] = tot ++;
for (int i = 0; i < g[x].size(); i++)
{
int t = g[x][i];
if( t == fa ) continue;
dfs(t,x);
}
out[x] = tot - 1;
}
题目:给定一棵以1为根的树,有两种操作。操作1将节点的x的权值异或1,操作2询问以x为根的子树的节点权值和。
思路:利用dfs序将子树询问变为区间和,树状数组维护即可。
#include <cstdio>
#include <vector>
using namespace std;
vector<int> g[100005];
int n,num[100005];
int in[100005],out[100005],c[100005];
int tot = 1;
void dfs(int x,int fa)
{
in[x] = tot ++;
for (int i = 0; i < g[x].size(); i++)
{
int t = g[x][i];
if( t == fa ) continue;
dfs(t,x);
}
out[x] = tot - 1;
}
int lowbit(int x)
{
return x & (-x);
}
void update(int x,int k)
{
for (int i = x; i <= n; i += lowbit(i))
{
c[i] += k;
}
}
int query(int x)
{
int res = 0;
for (int i = x; i > 0; i-= lowbit(i))
{
res += c[i];
}
return res;
}
int main()
{
scanf("%d",&n);
for (int i = 1; i < n; i++)
{
int x,y;
scanf("%d%d",&x,&y);
g[x].push_back(y);
}
dfs(1);
for (int i = 1; i <= n; i++)
{
update(i,1);
num[i] = 1;
}
int m;
scanf("%d",&m);
for (int i = 1; i <= m; i++)
{
getchar();
char t;
int x;
scanf("%c%d",&t,&x);
if( t == 'Q' )
{
printf("%d\n",query(out[x]) - query(in[x]-1));
}else
{
if( num[x] == 0 )
{
num[x] = 1;
update(in[x],1);
}else
{
num[x] = 0;
update(in[x],-1);
}
}
}
return 0;
}