[樹剖] 200615Practice T2 CF1017G The Tree

傳送門
這題就很妙,第一次看到維護最大後綴和的做法

有三個操作:

  1. 從一個點往下染黑,是黑色節點就繼續染,一直染到白色節點爲止;
  2. 染白一棵子樹;
  3. 查詢一個點的顏色。

對於一個點,狂染黑
染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時,
//我們只修改一個點而不是所有相關的點
//因此,當我們覆蓋一個子樹
//我們可能忽略了上面圓點的影響

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章