CF1017G The Tree(统计+树剖)
给定一棵树,维护以下 \(3\) 个操作:
1 x
如果节点 \(x\) 为白色,则将其染黑。否则对这个节点的所有儿子递归进行相同操作2 x
将以节点 \(x\) 为根的子树染白。3 x
查询节点 \(x\) 的颜色\(n,q\le 10^5\)。
\(\bigstar\texttt{Hint-1}\):如果真的向题目所说的去给每个点染色,将非常难维护。如果发现题目中信息混乱不妨向统计学方向考虑,我们记下这个 \(x\) 向下多少深度被染色,在每个点上根据它与根的路径查询答案。
如果没有 \(2\) 操作,我们在每个点上记录在它身上加了几次,那么查询时询问根到它的路径是否存在一段后缀和等于这段后缀的长度。不妨用一个更优美的解决方法,将每个点初始值设为 \(-1\),那么查询是否有一段后缀和 \(\ge 0\)。
\(\bigstar\texttt{Hint-2}\):那么 \(2\) 操作呢?首先将子树内的值都清空,权值赋为初值 \(-1\);并且需要去掉上面的点对子树内的贡献,就在 \(x\) 处加上从上面下来的影响深度(到 \(x\) 的最大后缀和)即可。
也就是在 \(x\) 处减去 \(query(x)+1\)。
另一种操作是离线后处理,但比较麻烦。
剩下的就是树剖入门题了,没什么好康的。
#define Maxn 100005
int n,q,tot,Time;
int hea[Maxn],nex[Maxn<<1],ver[Maxn<<1];
int dfnl[Maxn],dfnr[Maxn],siz[Maxn],dep[Maxn],bigson[Maxn],tp[Maxn],fa[Maxn];
inline void addedge(int x,int y){ ver[++tot]=y,nex[tot]=hea[x],hea[x]=tot; }
/************************** 线段树 + 树剖 *******************************/
struct TREE
{
ll sum,maxlast; bool laztag;
TREE(){ sum=maxlast=-1; laztag=false; }
inline void Imply(ll x) { laztag=true,sum=-x,maxlast=-1; }
inline void Push(ll val) { sum+=val,maxlast=sum; }
};
TREE tree[Maxn<<2];
inline TREE merge(TREE L,TREE R)
{
TREE ret;
ret.sum=L.sum+R.sum;
ret.maxlast=max(R.maxlast,L.maxlast+R.sum);
return ret;
}
inline void pushdown(int p,int nl,int nr)
{
if(tree[p].laztag)
{
int mid=(nl+nr)>>1;
tree[p<<1].Imply(mid-nl+1);
tree[p<<1|1].Imply(nr-mid);
tree[p].laztag=false;
}
}
void add(int p,int nl,int nr,int x,ll val)
{
if(nl==nr) { tree[p].Push(val); return; }
int mid=(nl+nr)>>1; pushdown(p,nl,nr);
if(mid>=x) add(p<<1,nl,mid,x,val);
else add(p<<1|1,mid+1,nr,x,val);
tree[p]=merge(tree[p<<1],tree[p<<1|1]);
}
void build(int p,int nl,int nr)
{
if(nl==nr) return;
int mid=(nl+nr)>>1;
build(p<<1,nl,mid),build(p<<1|1,mid+1,nr);
tree[p]=merge(tree[p<<1],tree[p<<1|1]);
}
ll query(int p,int nl,int nr,int l,int r)
{
if(nl>=l && nr<=r) return tree[p].sum;
int mid=(nl+nr)>>1; ll ret=0;
pushdown(p,nl,nr);
if(mid>=l) ret+=query(p<<1,nl,mid,l,r);
if(mid<r) ret+=query(p<<1|1,mid+1,nr,l,r);
return ret;
}
void change(int p,int nl,int nr,int l,int r)
{
if(nl>=l && nr<=r) { tree[p].Imply(nr-nl+1); return; }
int mid=(nl+nr)>>1; pushdown(p,nl,nr);
if(mid>=l) change(p<<1,nl,mid,l,r);
if(mid<r) change(p<<1|1,mid+1,nr,l,r);
tree[p]=merge(tree[p<<1],tree[p<<1|1]);
}
TREE color(int p,int nl,int nr,int l,int r)
{
if(nl>=l && nr<=r) return tree[p];
int mid=(nl+nr)>>1; pushdown(p,nl,nr);
if(mid>=l && mid<r)
return merge(color(p<<1,nl,mid,l,r),color(p<<1|1,mid+1,nr,l,r));
else if(mid>=l) return color(p<<1,nl,mid,l,r);
else return color(p<<1|1,mid+1,nr,l,r);
}
inline ll colorpath(int x)
{
TREE ret; bool exist=false;
while(tp[x]!=1)
{
if(!exist) ret=color(1,1,n,dfnl[tp[x]],dfnl[x]),exist=true;
else ret=merge(color(1,1,n,dfnl[tp[x]],dfnl[x]),ret);
x=fa[tp[x]];
}
if(!exist) ret=color(1,1,n,1,dfnl[x]),exist=true;
else ret=merge(color(1,1,n,1,dfnl[x]),ret);
return ret.maxlast;
}
void dfs1(int x)
{
siz[x]=1;
for(int i=hea[x];i;i=nex[i])
{
dep[ver[i]]=dep[x]+1,fa[ver[i]]=x,dfs1(ver[i]);
if(siz[ver[i]]>siz[bigson[x]]) bigson[x]=ver[i];
siz[x]+=siz[ver[i]];
}
}
void dfs2(int x,int T)
{
tp[x]=T,dfnl[x]=++Time;
if(bigson[x]) dfs2(bigson[x],T);
for(int i=hea[x];i;i=nex[i]) if(ver[i]!=bigson[x]) dfs2(ver[i],ver[i]);
dfnr[x]=Time;
}
/**********************************************************************/
int main()
{
n=rd(),q=rd();
for(int i=2,F;i<=n;i++) F=rd(),addedge(F,i);
dep[1]=1,dfs1(1),dfs2(1,1);
build(1,1,n); ll tmp;
for(int i=1,opt,x;i<=q;i++)
{
opt=rd(),x=rd();
if(opt==1) add(1,1,n,dfnl[x],1);
else if(opt==2)
{
tmp=colorpath(x),add(1,1,n,dfnl[x],-tmp-1);
if(dfnl[x]<dfnr[x]) change(1,1,n,dfnl[x]+1,dfnr[x]);
}
else printf((colorpath(x)>=0)?"black\n":"white\n");
}
return 0;
}