6729. 【2020.06.16省選模擬】T3 樹論

題目


正解

一個節點的SGSG值是它子樹中的點到它的最大距離(即最大深度)。
對於所有的根,可以發現一個節點的子樹內最大深度至多有兩種。具體證明可以考慮兩種最大深度從兩個不同的方向伸出,父親邊至多會佔據一個方向。

先將ilnil的做法:
對於每個節點記下這兩個最大深度以及它們的方向。
11爲根建樹。假設當前的根爲rtrt,可以發現只有11rtrt路徑上的點的最大深度方向不是向下。
樹鏈剖分,對於重鏈上的點,處理出不在重兒子方向上的最大深度;對於輕鏈,找到不是往rtrt方向的最大深度。
相應地在數據結構上維護即可。

題解的做法更加妙:找出直徑的中點,可以證明直徑的中點只有一個(如果直徑的長度爲奇數,則將中間那條邊拆成兩條邊和一個虛點)。
對於每個點(根節點除外),可以發現它的兩個最大深度分別是子樹內最大深度,以及到其中一個端點的距離。也就是相當於一個往下一個往上。
當根節點變爲其它點的時候,直徑中點到它路徑上的點的最大深度都向上指,其它的點的最大深度都向下指。
隨便樹剖一下,這樣就可以更加方便地維護了。

具體怎樣在數據結構上維護呢……
每個點有兩種取值,一次換方向相當於這兩個取值之間切換。
並且每個點有選或不選兩種狀態,選了纔會對答案造成貢獻。
一次換根操作需要切換取值,一次修改就是切換選或不選的狀態。
於是這可以抽象成一個222*2的矩陣,橫座標表示選或不選,縱座標表示是當前使用的取值還是當前不用的取值。
維護的時候就是支持行翻轉和列翻轉的操作,然後維護總體的異或和。這個東西用線段樹簡單維護。
總時間複雜度O(nlg2n)O(n \lg^2 n)


代碼

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010
int input(){
	char ch=getchar();
	while (ch<'0' || ch>'9')
		ch=getchar();
	int x=0;
	do{
		x=x*10+ch-'0';
		ch=getchar();
	}
	while ('0'<=ch && ch<='9');
	return x;
}
int n,m;
struct EDGE{
	int to;
	EDGE *las;
} e[N*2];
int ne;
EDGE *last[N];
int L,R,len;
int rt,rt_;
int f[N],g[N];
int fa[N],dep[N],siz[N],hs[N],top[N],in[N],out[N],nowdfn,re[N];
void getdis(int x){
	dep[x]=dep[fa[x]]+1;
	for (EDGE *ei=last[x];ei;ei=ei->las)
		if (ei->to!=fa[x])
			fa[ei->to]=x,getdis(ei->to);
}
void dfs1(int x){
	siz[x]=1;
	for (EDGE *ei=last[x];ei;ei=ei->las)
		if (ei->to!=fa[x]){
			fa[ei->to]=x;
			dep[ei->to]=dep[x]+1;
			dfs1(ei->to);
			siz[x]+=siz[ei->to];
			if (siz[ei->to]>siz[hs[x]])
				hs[x]=ei->to;
		}
}
void dfs2(int x,int t){
	in[x]=++nowdfn;
	re[nowdfn]=x;
	top[x]=t;
	if (hs[x])
		dfs2(hs[x],t);
	for (EDGE *ei=last[x];ei;ei=ei->las)
		if (ei->to!=fa[x] && ei->to!=hs[x])
			dfs2(ei->to,ei->to);
	out[x]=nowdfn;
}
void init(int x){
	f[x]=0;
	g[x]=(in[x]<=in[L] && in[L]<=out[x]?dep[R]:dep[L])+dep[x]-(len&1);
	for (EDGE *ei=last[x];ei;ei=ei->las)
		if (ei->to!=fa[x]){
			init(ei->to);
			f[x]=max(f[x],f[ei->to]+1);
		}
}
struct Info{
	int m[2][2];
	void swap0(){swap(m[0][0],m[1][0]),swap(m[0][1],m[1][1]);}
	void swap1(){swap(m[0][0],m[0][1]),swap(m[1][0],m[1][1]);}
};
Info operator^(Info &a,Info &b){
	return {a.m[0][0]^b.m[0][0],a.m[0][1]^b.m[0][1],a.m[1][0]^b.m[1][0],a.m[1][1]^b.m[1][1]};
}
Info s[N*4];
int r0[N*4],r1[N*4];
int *ans;
void build(int k,int l,int r){
	if (l==r){
		int x=re[l];
		s[k]={f[x],0,g[x],0};
		return;
	}
	int mid=l+r>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	s[k]=s[k<<1]^s[k<<1|1];
}
void pd(int k){
	if (r0[k]){
		s[k<<1].swap0(),s[k<<1|1].swap0();
		r0[k<<1]^=1,r0[k<<1|1]^=1;
		r0[k]=0;
	}
	if (r1[k]){
		s[k<<1].swap1(),s[k<<1|1].swap1();
		r1[k<<1]^=1,r1[k<<1|1]^=1;
		r1[k]=0;
	}
}
void change0(int k,int l,int r,int st,int en){
	if (st<=l && r<=en){
		s[k].swap0();
		r0[k]^=1;
		return;
	}
	pd(k);
	int mid=l+r>>1;
	if (st<=mid)
		change0(k<<1,l,mid,st,en);
	if (mid<en)
		change0(k<<1|1,mid+1,r,st,en);
	s[k]=s[k<<1]^s[k<<1|1];
}
void change1(int k,int l,int r,int st,int en){
	if (st<=l && r<=en){
		s[k].swap1();
		r1[k]^=1;
		return;
	}
	pd(k);
	int mid=l+r>>1;
	if (st<=mid)
		change1(k<<1,l,mid,st,en);
	if (mid<en)
		change1(k<<1|1,mid+1,r,st,en);
	s[k]=s[k<<1]^s[k<<1|1];
}
void mroot(int t){
	int u=rt_,v=t;
	for (;top[u]!=top[v];u=fa[top[u]]){
		if (dep[top[u]]<dep[top[v]])
			swap(u,v);
		change0(1,1,n,in[top[u]],in[u]);
	}
	if (dep[u]<dep[v])
		swap(u,v);
	if (u!=v)
		change0(1,1,n,in[hs[v]],in[u]);
	rt_=t;
}
void workchain(int u,int v){
	for (;top[u]!=top[v];u=fa[top[u]]){
		if (dep[top[u]]<dep[top[v]])
			swap(u,v);
		change1(1,1,n,in[top[u]],in[u]);
	}
	if (dep[u]<dep[v])
		swap(u,v);
	change1(1,1,n,in[v],in[u]);
}
void worksubtree(int u){
	if (u==rt_)
		change1(1,1,n,1,n);
	else if (in[u]<=in[rt_] && in[rt_]<=out[u]){
		int x=hs[u];
		if (!(in[x]<=in[rt_] && in[rt_]<=out[x])){
			x=rt_;
			for (;fa[top[x]]!=u;x=fa[top[x]]);
			x=top[x];
		}
		if (1<=in[x]-1)
			change1(1,1,n,1,in[x]-1);
		if (out[x]+1<=n)
			change1(1,1,n,out[x]+1,n);
	}
	else
		change1(1,1,n,in[u],out[u]);
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=input(),m=input();
	for (int i=1;i<n;++i){
		int u=input(),v=input();
		e[ne]={v,last[u]};
		last[u]=e+ne++;
		e[ne]={u,last[v]};
		last[v]=e+ne++;
	}
	fa[1]=0,getdis(1);
	L=1,R=1;
	for (int i=2;i<=n;++i)
		if (dep[i]>dep[L])
			L=i;
	fa[L]=0,getdis(L);
	for (int i=2;i<=n;++i)
		if (dep[i]>dep[R])
			R=i;
			
	len=dep[R]-1;
	if (len&1){
		int x=R,y;
		for (int i=0;i<len>>1;++i)
			x=fa[x];
		y=fa[x];
		rt=++n;
		for (EDGE *ei=last[x];ei;ei=ei->las)
			if (ei->to==y){
				ei->to=rt;
				break;
			}
		for (EDGE *ei=last[y];ei;ei=ei->las)
			if (ei->to==x){
				ei->to=rt;
				break;
			}
		e[ne]={x,last[rt]};
		last[rt]=e+ne++;
		e[ne]={y,last[rt]};
		last[rt]=e+ne++;
	}
	else{
		rt=R;
		for (int i=0;i<len>>1;++i)
			rt=fa[rt];	
	}
	fa[rt]=0,dep[rt]=0,dfs1(rt);
	dfs2(rt,rt);
	init(rt);
	if (len&1)
		f[rt]=0,g[rt]=0;
	rt_=rt;
	ans=&s[1].m[0][0];
	build(1,1,n);
	mroot(1);
	while (m--){
		int op;
		scanf("%d",&op);
		if (op==1){
			int u,v,x;
			scanf("%d%d%d",&u,&v,&x);
			workchain(u,v);
			mroot(x);
		}
		else{
			int u,x;
			scanf("%d%d",&u,&x);
			worksubtree(u);
			mroot(x);
		}
		printf("%d\n",*ans);
	}
	return 0;
}


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