线段树……
题意:给定一棵树,以1为根,初始所有节点为0,有三种操作:
1、给节点x灌水,即x以及子树赋值为1;
2、给节点x排水,即x以及祖先赋值为0;
3、查询节点x是否有水……
样例:
Examples
input
5
1 2
5 1
2 3
4 2
12
1 1
2 3
3 1
3 2
3 3
3 4
1 2
2 4
3 1
3 3
3 4
3 5
output
0
0
0
1
0
1
0
1
对于操作1,首先查询节点x的子树里是否有0,如果有则将0“移动”到x的父亲,再进行区间修改;
对于操作2,单点修改为0即可;
对于操作3,查询x的子树是否有0即可;
代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#define maxn 500000+10
#define _ const int&
#define mid ((l+r)>>1)
#define lc (t<<1)
#define rc ((t<<1)^1)
#define Moon for (int k=head[x];k;k=b[k].next)
using namespace std;
struct xx
{
int v,next;
}b[maxn<<1];
int ll,rr,v,tag[maxn<<2],//子树被灌满水
sz[maxn],num[maxn],dfn[maxn],T,Q,fa[maxn],x,y,n,head[maxn],m;
void add(_ u,_ v)
{
b[++m]=(xx){v,head[u]};
head[u]=m;
}
void dfs(_ x)//建树
{
dfn[x]=++T;sz[x]=1;
Moon
{
_ v=b[k].v;
if (v==fa[x])continue;
fa[v]=x;
dfs(v);
sz[x]+=sz[v];
}
}
void maintain(_ t)//维护节点权值
{
tag[t]=(tag[t<<1]==1)&&(tag[(t<<1)^1]==1)?1:0;
}
void pushdown(_ t)//下传标记
{
if (tag[t]==0) return ;
tag[lc]=tag[rc]=1;
tag[t]=0;
return ;
}
int find(_ l,_ r,_ t)//查询子树是否有0
{
if (ll<=l&&r<=rr) return !tag[t];
int ans=0;
pushdown(t);
if (ll<=mid) ans=ans||find(l,mid,lc);
if (mid<rr) ans=ans||find(mid+1,r,rc);
maintain(t);
return ans;
}
int update1(_ l,_ r,_ t)//区间[ll,rr]修改为1
{
if (ll<=l&&r<=rr) return tag[t]=1;;
pushdown(t);
if (ll<=mid) update1(l,mid,lc);
if (mid<rr) update1(mid+1,r,rc);
maintain(t);
}
int update2(_ l,_ r,_ t)//单点修改v为0
{
//pushdown(t);//不要乱写pushdown这里坑了我灰常久……
if (l==r) return tag[t]=0;
pushdown(t);
v<=mid? update2(l,mid,lc):update2(mid+1,r,rc);
maintain(t);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs(1);
scanf("%d",&Q);
while (Q--)
{
scanf("%d%d",&x,&y);
ll=dfn[y],rr=dfn[y]+sz[y]-1;
if (x==1)
{
if (fa[y]&&find(1,n,1)) v=dfn[fa[y]],update2(1,n,1);
update1(1,n,1);
}
if (x==2) v=dfn[y],update2(1,n,1);
if (x==3)find(1,n,1) ?puts("0"): puts("1");
}
return 0;
}