Danil and a Part-time Job--线段树dfs序

题目链接:https://cn.vjudge.net/problem/CodeForces-877E

题目大意

有一棵以1位根节点的树,每个节点都有一盏灯,或亮或灭,m次操作,get  x,表示求x以及其子树中,所有亮着的灯的数量,pow  x,表示把x以及其子树灯的状态改变,即1变为0,0变为1。

分析

通过这个题学会了dfs序。dfs序的思想就是,从根节点开始dfs,记下每个节点第一次遇到以及最后一次遇到时的序号,数组in[]表示第一次遇到,数组out[]表示最后一次遇到。那么在遍历的过程当中,当走到某个节点的时候,一定是先把它的子树遍历完,然后返回到上一级,假设遇到x节点的时候,序号是2,x子树共有4个节点,当遍历完所有子树节点时,序号变为6,那么2--6这个区间就可以表示x以及其子树,这样就可以放到线段树里面解决问题了。

Danil and a Part-time Job--线段树dfs序_第1张图片

如图,假设先从1开始,先走2,那么第一次遇到2号节点的时候,序号是2,其次是子树,4号节点对应序号3,5号对应4,6号对应5,那么2--5,就是2号节点及其子树的区间这样就能放到线段树里面了。

代码

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int N = 200010;
int n, m, cnt, tot;
int in[N], out[N], head[N], t[N], num[N];
struct node{
	int l, r, sum, lazy;
}tr[N<<2];
struct Edge{
	int to, nxt;
}edge[N];
void add(int a, int b)
{
	edge[cnt].to = b;
	edge[cnt].nxt = head[a];
	head[a] = cnt++;
}
void pushup(int m)
{
	tr[m].sum = tr[m<<1].sum + tr[m<<1|1].sum;
}
void pushdown(int m)
{
	if(tr[m].lazy)
	{
		tr[m<<1].lazy ^= 1;
		tr[m<<1|1].lazy ^= 1;
		tr[m<<1].sum = tr[m<<1].r - tr[m<<1].l + 1 - tr[m<<1].sum;
		tr[m<<1|1].sum = tr[m<<1|1].r - tr[m<<1|1].l + 1 - tr[m<<1|1].sum;
		tr[m].lazy = 0;
	}
}
void build(int m, int l, int r)
{
	tr[m].l = l;
	tr[m].r = r;
	tr[m].lazy = 0;
	if(l == r)
	{
		tr[m].sum = t[num[l]];
		return ;
	}
	int mid = (l + r) >> 1;
	build(m<<1, l, mid);
	build(m<<1|1, mid + 1, r);
	pushup(m);
}
void updata(int m, int l, int r)
{
	if(tr[m].l == l && tr[m].r == r)
	{
		tr[m].sum = r - l + 1 - tr[m].sum;
		tr[m].lazy ^= 1;
		return ;
	}
	pushdown(m);
	int mid = (tr[m].l + tr[m].r) >> 1;
	if(r <= mid) updata(m<<1, l, r);
	else if(l > mid) updata(m<<1|1, l, r);
	else
	{
		updata(m<<1, l, mid);
		updata(m<<1|1, mid + 1, r);
	}
	pushup(m);
}
int query(int m, int l, int r)
{
	if(tr[m].l == l && tr[m].r == r) return tr[m].sum;
	pushdown(m);
	int mid = (tr[m].l + tr[m].r) >> 1;
	if(r <= mid) return query(m<<1, l, r);
	else if(l > mid) return query(m<<1|1, l, r);
	else return query(m<<1, l, mid) + query(m<<1|1, mid + 1, r);
}
void dfs(int x)
{
	in[x] = ++tot;//第一次遇到 
	num[tot] = x;//num[tot]=x表示在新的序列中,tot这个点是原先的x这个点 
	for(int i = head[x]; i != -1; i = edge[i].nxt)
		dfs(edge[i].to);
	out[x] = tot;//最后一次遇到 
}
int main()
{
	scanf("%d", &n);
	cnt = tot = 0;
	memset(head, -1, sizeof head);
	for(int i = 2; i <= n; i++)
	{
		int p;
		scanf("%d", &p);
		add(p, i);
	}
	for(int i = 1; i <= n; i++) scanf("%d", &t[i]);
	dfs(1);
	build(1, 1, tot);
	scanf("%d", &m);
	char s[2];
	int x;
	while(m--)
	{
		scanf("%s %d", &s, &x);
		if(s[0] == 'g') printf("%d\n", query(1, in[x], out[x]));
		else updata(1, in[x], out[x]);
	}
	return 0;
}

 

你可能感兴趣的:(线段树)