传送门
这题就很妙,第一次看到维护最大后缀和的做法
有三个操作:
- 从一个点往下染黑,是黑色节点就继续染,一直染到白色节点为止;
- 染白一棵子树;
- 查询一个点的颜色。
对于一个点,狂染黑
染n次,最多对比它深n-1层的点造成影响
不知怎么就想到:这个节点下面的点对这个节点染色的贡献为-1,就像一个最大后缀和
整理一下语言就是对于v和它的祖先u,u染黑n次,u,v之间点对这个染色“弱化”了距离次,如果距离>染黑次数,那么v没有被影响到,反之影响到
于是考虑所有点最初权值-1,染黑权值+1,染白权值-1,查询最大后缀和(线段树维护),后缀和<0黑,>=0白
#include<bits/stdc++.h>
using namespace std;
#define in Read()
inline int in{
int i=0,f=1;char ch=0;
while(ch!='-'&&!isdigit(ch)) ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e5+5;
const int INF=1e9+7;
int n,q;
int tot;
int ord;
int first[NNN];
int nxt[NNN<<1];
int aim[NNN<<1];
int faz[NNN];
int son[NNN];
int top[NNN];
int siz[NNN];
int dfn[NNN];
int dep[NNN];
struct Tree{
int l,r;
int bmx;//back max
int sum;
int cov;//cover tag
inline void merge(Tree l,Tree r){
sum=l.sum+r.sum;
bmx=max(r.bmx,r.sum+l.bmx);
}
}tre[NNN<<2],ans;
inline void add(int u,int v){
++tot;
nxt[tot]=first[u];
first[u]=tot;
aim[tot]=v;
return;
}
inline void DFS1(int fa,int u){
faz[u]=fa;
siz[u]=1;
dep[u]=dep[fa]+1;
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
DFS1(u,v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
return;
}
inline void DFS2(int u,int tp){
top[u]=tp;
dfn[u]=++ord;
if(!son[u]) return;
DFS2(son[u],tp);
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(v==son[u]) continue;
DFS2(v,v);
}
return;
}
#define lch p<<1
#define rch p<<1|1
#define tl tre[p].l
#define tr tre[p].r
inline void push_down(int p){
if(!tre[p].cov) return;
int len=tr-tl+1;
tre[lch].bmx=-1;
tre[rch].bmx=-1;
tre[lch].sum=-1*(len-(len>>1));
tre[rch].sum=-1*(len>>1);
tre[lch].cov=tre[p].cov;
tre[rch].cov=tre[p].cov;
tre[p].cov=0;
return;
}
inline void build(int p,int l,int r){
tl=l,tr=r;
tre[p].sum=-(r-l+1);
tre[p].bmx=-1;
tre[p].cov=0;
if(l==r) return;
int mid=l+r>>1;
build(lch,l,mid);
build(rch,mid+1,r);
tre[p].merge(tre[lch],tre[rch]);
return;
}
inline void update(int p,int d,int w){
if(tl==tr){
tre[p].sum+=w;
tre[p].bmx+=w;
return;
}
push_down(p);
int mid=tl+tr>>1;
if(d<=mid) update(lch,d,w);
else update(rch,d,w);
tre[p].merge(tre[lch],tre[rch]);
}
inline void cover(int p,int l,int r){
if(l<=tl&&tr<=r){
tre[p].bmx=-1;
tre[p].sum=-(tr-tl+1);
tre[p].cov=1;
return;
}
push_down(p);
int mid=tl+tr>>1;
if(l<=mid) cover(lch,l,r);
if(r>mid) cover(rch,l,r);
tre[p].merge(tre[lch],tre[rch]);
return;
}
inline Tree query(int p,int l,int r){
if(l<=tl&&tr<=r) return tre[p];
push_down(p);
int mid=tr+tl>>1;
if(r<=mid) return query(lch,l,r);
if(l>mid) return query(rch,l,r);
Tree res;
res.merge(query(lch,l,r),query(rch,l,r));
return res;
}
#undef lch
#undef rch
#undef tl
#undef tr
inline void Inquire(int u){
ans.sum=0,ans.bmx=-1;
while(top[u]!=1){
ans.merge(query(1,dfn[top[u]],dfn[u]),ans);
u=faz[top[u]];
}
ans.merge(query(1,1,dfn[u]),ans);
return;
}
int main(){
n=in,q=in;
for(int v=2;v<=n;++v){
int u=in;
add(u,v);
}
DFS1(0,1);
DFS2(1,1);
build(1,1,n);
for(int i=1;i<=q;++i){
int opt=in,u=in;
if(opt==1){
update(1,dfn[u],1);
}else if(opt==2){
cover(1,dfn[u],dfn[u]+siz[u]-1);
//remove the influence of the upper dots
//which is because when we do operation 1,
//we only modify one dot instead of all related dots
//therefore when we cover a sub-tree
//we probably ignore the influence of upper dots
Inquire(u);
if(ans.bmx>=0) update(1,dfn[u],-ans.bmx-1);//ans.bmx(originally search result)-ans.bmx-1=-1
}else if(opt==3){
Inquire(u);
if(ans.bmx>=0) puts("black");
else puts("white");
}
}
return 0;
}
注释的翻译:
//去掉上面的点的影响
//这是因为当我们做操作1时,
//我们只修改一个点而不是所有相关的点
//因此,当我们覆盖一个子树
//我们可能忽略了上面圆点的影响