题目链接:http://codeforces.com/problemset/problem/343/D
题目大意:
有n个水库,编号1到n,它们之间互相连接形成一颗树,然后对水库进行以下操作,为了方便,接下来用结点说明。
操作1,将某个结点及它的儿子结点赋为1.
操作2,将某个结点及它的父亲结点赋为0.
操作3,查询某个结点当前的值.
解题思路:
因为之前做过一道类似的题目,很快就明白首先要将树的结构转换为线性的结构。具体的操作就是将树遍历一遍然后找出每个结点对应的所管辖的区间。这个操作也比较容易实现,但是这个题目有一点卡了很久,就是将某个结点以及它的父亲结点赋为0,当时想用同样的方法用区间表示,想了一个多小时都没想出来该怎么处理。想了几种方法均有漏洞。实在无果,去网上喵了一眼正确解法,发现了这题的奥妙所在。
因为如果将某个结点置为0,那么它的父亲结点应该都是0.这样的话我们更新的时候就可以先只更新这个结点值为0.然后查询某个结点值的时候,直接查询它所管辖区间即它的所有儿子中是否含有为0的结点,如果有就,该节点值即为0.
另外一个比较重要的操作就是操作1.当我们执行操作1的时候,要首先查询它的儿子结点中是否有0结点。如果有的话,就先将当前要更新结点的父亲结点(这个需要专门记录一下)的值更新为0,这是很关键的一步。保证了这个思路的正确性。
之后就是线段树的正常更新了,以下贴代码,注意因为codeforce是单组测试所以没有进行vis数组和vector的初始化,
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#include<vector>
#define pb push_back
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int N=500010;
int n,m,idx,fl;
struct node
{
int lv,rv; //记录它当前管辖区间的左右端点
int fa; //记录它的父亲结点
}a[N];
bool vis[N];
vector<int> v[N]; //建图
struct tree
{
int l,r,mid;
int lazy;
int cnt;
}t[N<<2];
void build(int l,int r,int rt) //建树
{
int m=(l+r)>>1;
t[rt].l=l;t[rt].r=r;
t[rt].mid=m;
t[rt].cnt=0;
t[rt].lazy=0;
if(l==r)
return ;
build(l,m,lson);
build(m+1,r,rson);
}
void pushup(int rt) //如果子节点有0 则为0
{
if(t[lson].cnt==0||t[rson].cnt==0)
t[rt].cnt=0;
else
t[rt].cnt=1;
}
void pushdown(int rt) //延迟更新
{
if(t[rt].lazy)
{
t[lson].cnt=t[rson].cnt=t[rt].cnt;
t[lson].lazy=t[rson].lazy=1;
t[rt].lazy=0;
}
}
void update(int p,int q,int flag,int rt) //正常的线段树更新
{
if(p<=t[rt].l&&t[rt].r<=q)
{
t[rt].cnt=flag;
t[rt].lazy=1;
return ;
}
pushdown(rt);
if(p<=t[rt].mid)
update(p,q,flag,lson);
if(q>t[rt].mid)
update(p,q,flag,rson);
pushup(rt);
}
void query(int p,int q,int rt) //正常的线段树查询
{
if(fl)
return ;
if(t[rt].l>=p&&t[rt].r<=q)
{
if(t[rt].cnt==0)
fl=1;
return ;
}
pushdown(rt);
if(p<=t[rt].mid)
query(p,q,lson);
if(q>t[rt].mid)
query(p,q,rson);
pushup(rt);
}
void dfs(int k,int fa) //遍历树 将树转换为线性结构
{
a[k].lv=idx++; //记录左端点
vis[k]=1;
a[k].fa=fa; //记录父亲结点
for(int i=0;i<(int)v[k].size();i++)
{
if(!vis[v[k][i]])
dfs(v[k][i],k);
}
a[k].rv=idx-1; //记录右端点
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
int p,q;
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&p,&q);
v[p].pb(q); //建图
v[q].pb(p);
}
build(1,n,1);
idx=1;
dfs(1,0); //遍历
scanf("%d",&m);
while(m--) //跟解题思路里面说的步骤一样
{
scanf("%d%d",&p,&q);
if(p==1)
{
fl=0;
query(a[q].lv,a[q].rv,1);
if(fl)
update(a[a[q].fa].lv,a[a[q].fa].lv,0,1);
update(a[q].lv,a[q].rv,1,1);
}
if(p==2)
update(a[q].lv,a[q].lv,0,1);
if(p==3)
{
fl=0;
query(a[q].lv,a[q].rv,1);
if(fl)
printf("0\n");
else
printf("1\n");
}
}
}
}