點分樹學習筆記

liuchanglc發表於2021-01-16

點分樹

點分樹借鑑了點分治的思想,將分治過程中具有父子關係的重心連邊

形成了一顆高度為 \(logn\) 的樹

這樣每一次分治時,我們就不用重新去找重心,直接沿著點分樹向上跳即可

和點分治相比,點分樹可以支援多組詢問

而且還能夠進行修改操作,可以解決一些強制線上的題目

點分樹有兩個性質:

\(1\)、原樹上任意兩點 \(p,q\) 在點分樹上的 \(lca\) 一定在 \(p\)\(q\) 的路徑上

\(2\)、點分樹的樹高是 \(logn\) 級別的

第一個性質決定了我們可以通過向上跳父親來處理與路徑有關的資訊

第二個性質則決定了我們這麼做不會 \(T\)

一般來說,點分樹的題都需要我們在節點上維護兩個資料結構

一個儲存所有子樹對自己的貢獻,另一個儲存所有子樹對父親的貢獻,計算答案時要容斥計算

因為樹高是 \(logn\) 的,所以這麼做的空間複雜度為 \(nlogn\)

二維陣列肯定開不下,所以經常用 \(vector\) 來實現

P6329 【模板】點分樹 | 震波

題目傳送門

分析

查詢到點 \(x\) 距離小於等於 \(k\) 的所有點的權值之和,強制線上,支援修改

根據點分樹的第一個性質,我們可以列舉其它點與點 \(x\)\(lca\) 是哪一個點,這個 \(lca\) 一定是點 \(x\) 在點分樹上的祖先節點

假設當前列舉到的點是 \(y\)

\(y\) 滿足條件,則有 \(dis(x,lca)+dis(y,lca) \leq k\)

所以只需要查詢到 \(lca\) 的距離小於等於 \(k-dis(x,lca)\) 的點即可

還要支援修改操作,可以用數狀陣列維護

但是這樣會有重複的情況

因為我們只是簡單地滿足了距離關係,所以 \(x\)\(y\) 在原樹上的 \(lca\) 不一定是當前的節點,還有可能是當前點在點分樹上的子節點

因此要把子節點中距離 \(lca\) 的距離小於等於 \(k-dis(c,lca)\) 的貢獻刪除,這和點分治中的容斥是一樣的

但是要注意的是,點分樹中的父子節點在原樹上基本沒有什麼關係,所以不能直接在兒子節點的數狀陣列中把這個貢獻減去

而要另開一個數狀陣列記錄兒子節點在點分樹中的子樹對其父親的貢獻

查詢時,我們從當前點開始一直跳點分樹的父親,容斥計算貢獻

修改時,也是暴力跳父親,在數狀陣列上加入當前貢獻與之前貢獻的差

對於一開始就有的價值,我們直接作為修改操作處理

時間複雜度 \(mlog^2n\)

程式碼

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e5+5;
int h[maxn],tot=1,n,m,latans;
struct asd{
	int to,nxt;
}b[maxn<<1];
void ad(rg int aa,rg int bb){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	h[aa]=tot++;
}
int siz[maxn],dep[maxn],fa[maxn],son[maxn];
void dfs1(rg int now,rg int lat){
	siz[now]=1;
	dep[now]=dep[lat]+1;
	fa[now]=lat;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		dfs1(u,now);
		siz[now]+=siz[u];
		if(son[now]==0 || siz[u]>siz[son[now]]) son[now]=u;
	}
}
int tp[maxn],val[maxn];
void dfs2(rg int now,rg int top){
	tp[now]=top;
	if(son[now]) dfs2(son[now],top);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==son[now] || u==fa[now]) continue;
		dfs2(u,u);
	}
}
int getlca(rg int xx,rg int yy){
	while(tp[xx]!=tp[yy]){
		if(dep[tp[xx]]<dep[tp[yy]]) std::swap(xx,yy);
		xx=fa[tp[xx]];
	}
	return dep[xx]<dep[yy]?xx:yy;
}
int getdis(rg int xx,rg int yy){
	return dep[xx]+dep[yy]-2*dep[getlca(xx,yy)];
}
int rt,totsiz,maxsiz[maxn];
bool vis[maxn];
void getroot(rg int now,rg int lat){
	siz[now]=1,maxsiz[now]=0;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat || vis[u]) continue;
		getroot(u,now);
		siz[now]+=siz[u];
		maxsiz[now]=std::max(maxsiz[now],siz[u]);
	}
	maxsiz[now]=std::max(maxsiz[now],totsiz-siz[now]);
	if(maxsiz[now]<maxsiz[rt]) rt=now;
}
int lb(rg int xx){
	return xx&-xx;
}
struct BIT{
	std::vector<int> tr;
	int trsiz;
	void init(rg int now){
		for(rg int i=0;i<=now;i++) tr.push_back(0);
		trsiz=now;
	}
	void ad(rg int wz,rg int val){
		wz=std::min(wz+1,trsiz);
		for(rg int i=wz;i<=trsiz;i+=lb(i)){
			tr[i]+=val;
		}
	}
	int cx(rg int wz){
		wz=std::min(wz+1,trsiz);
		rg int nans=0;
		for(rg int i=wz;i>0;i-=lb(i)){
			nans+=tr[i];
		}
		return nans;
	}
}tr1[maxn],tr2[maxn];
int newfa[maxn];
void getsiz(rg int now,rg int lat){
	siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat || vis[u]) continue;
		getsiz(u,now);
		siz[now]+=siz[u];
	}
}
void predfs(rg int now){
	vis[now]=1;
	getsiz(now,0);
	rg int tmp=siz[now]+3;
	tr1[now].init(tmp);
	tr2[now].init(tmp);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(vis[u]) continue;
		totsiz=siz[u],rt=0;
		getroot(u,now);
		newfa[rt]=now;
		predfs(rt);
	}
}//構建點分樹
void updat(rg int now,rg int nval){
	for(rg int i=now;i;i=newfa[i]){
		tr1[i].ad(getdis(now,i),nval);
	}
	for(rg int i=now;newfa[i];i=newfa[i]){
		tr2[i].ad(getdis(newfa[i],now),nval);
	}
}//更新答案
int solve(rg int now,rg int k){
	rg int nans=tr1[now].cx(k);
	for(rg int i=now;newfa[i];i=newfa[i]){
		rg int tmp=getdis(newfa[i],now);
		if(k>=tmp) nans+=tr1[newfa[i]].cx(k-tmp)-tr2[i].cx(k-tmp);
	}
	return nans;
}//查詢答案
int main(){
	memset(h,-1,sizeof(h));
	n=read(),m=read();
	for(rg int i=1;i<=n;i++) val[i]=read();
	rg int aa,bb,cc;
	for(rg int i=1;i<n;i++){
		aa=read(),bb=read();
		ad(aa,bb),ad(bb,aa);
	}
	dfs1(1,0);
	dfs2(1,1);
	maxsiz[0]=0x3f3f3f3f,rt=0,totsiz=n;
	getroot(1,0);
	predfs(rt);
	for(rg int i=1;i<=n;i++) updat(i,val[i]);
	for(rg int i=1;i<=m;i++){
		aa=read(),bb=read(),cc=read();
		bb^=latans,cc^=latans;
		if(aa==0) printf("%d\n",latans=solve(bb,cc));
		else {
			updat(bb,cc-val[bb]);
			val[bb]=cc;
		}
	}
	return 0;
}

P3241 [HNOI2015]開店

題目傳送門

分析

查詢點 \(u\) 到所有年齡在區間 \([l,r]\) 中妖怪的距離,強制線上

同樣可以在點分樹上套資料結構解決

設當前的點為 \(x\),當前點的兒子節點為 \(u\)

那麼該點對答案的貢獻就是\(x\)子樹中年齡在 \([l,r]\) 之間的妖怪到 \(x\) 的距離之和 - \(u\) 子樹中年齡在 \([l,r]\) 之間的妖怪到 \(x\) 的距離之和+(\(x\)子樹中年齡在 \([l,r]\) 之間的妖怪個數- \(u\) 子樹中年齡在 \([l,r]\) 之間的妖怪個數) \(\times\) 查詢點到節點 \(x\) 的距離

因為沒有修改操作,所以直接用 \(vector\)\(vector\) 中儲存妖怪的年齡以及妖怪到當前點的距離

\(vector\) 要開兩個,一個記錄對當前點的貢獻,一個記錄對父親節點的貢獻

對於年齡的限制,我們在建好點分樹後直接把 \(vector\) 中的元素按照年齡從小到大排序

然後記錄一個字首和,查詢時二分即可

而妖怪的個數就是二分時下標之差

距離之和直接暴力跳父親算一下就行了

程式碼

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<iostream>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e6+5;
int h[maxn],tot=1;
struct asd{
	int to,nxt,val;
}b[maxn];
void ad(rg int aa,rg int bb,rg int cc){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	b[tot].val=cc;
	h[aa]=tot++;
}
int n,q,maxage,x[maxn],siz[maxn],fa[maxn],dep[maxn],son[maxn];
long long dis[maxn];
void dfs1(rg int now,rg int lat){
	fa[now]=lat;
	dep[now]=dep[lat]+1;
	siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		dis[u]=dis[now]+b[i].val;
		dfs1(u,now);
		siz[now]+=siz[u];
		if(siz[son[now]]<siz[u]) son[now]=u;
	}
}
int tp[maxn];
void dfs2(rg int now,rg int top){
	tp[now]=top;
	if(son[now]) dfs2(son[now],top);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==son[now] || u==fa[now]) continue;
		dfs2(u,u);
	}
}
int getlca(rg int xx,rg int yy){
	while(tp[xx]!=tp[yy]){
		if(dep[tp[xx]]<dep[tp[yy]]) std::swap(xx,yy);
		xx=fa[tp[xx]];
	}
	return dep[xx]<dep[yy]?xx:yy;
}
long long getdis(rg int xx,rg int yy){
	return dis[xx]+dis[yy]-2LL*dis[getlca(xx,yy)];
}
int maxsiz[maxn],totsiz,rt;
bool vis[maxn];
void getroot(rg int now,rg int lat){
	maxsiz[now]=0,siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat || vis[u]) continue;
		getroot(u,now);
		siz[now]+=siz[u];
		maxsiz[now]=std::max(maxsiz[now],siz[u]);
	}
	maxsiz[now]=std::max(maxsiz[now],totsiz-siz[now]);
	if(maxsiz[rt]>maxsiz[now]) rt=now;
}
struct Node{
	int age;
	long long dis;
	Node(){}
	Node(rg int aa,rg long long bb){
		age=aa,dis=bb;
	}
	friend bool operator <(const Node& A,const Node& B){
		return A.age<B.age;
	}
};
std::vector<Node> vec[2][maxn];
void getsiz(rg int now,rg int lat){
	siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat || vis[u]) continue;
		getsiz(u,now);
		siz[now]+=siz[u];
	}
}
int newfa[maxn];
void predfs(rg int now){
	vis[now]=1;
	getsiz(now,0);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(!vis[u]){
			rt=0,totsiz=siz[u];
			getroot(u,now);
			newfa[rt]=now;
			predfs(rt);
		}
	}
}
void updat(rg int now){
	for(rg int i=now;i;i=newfa[i]){
		vec[0][i].push_back(Node(x[now],getdis(i,now)));
	}
	for(rg int i=now;newfa[i];i=newfa[i]){
		vec[1][i].push_back(Node(x[now],getdis(newfa[i],now)));
	}
}
long long latans;
int nowsiz;
long long cx(rg int op,rg int now,rg int l,rg int r){
	rg int nl=std::lower_bound(vec[op][now].begin(),vec[op][now].end(),Node(l,0))-vec[op][now].begin()-1;
	rg int nr=std::upper_bound(vec[op][now].begin(),vec[op][now].end(),Node(r,0))-vec[op][now].begin()-1;
	nowsiz=nr-nl;
	if(nr<0) return 0;
	if(nl<0) return vec[op][now][nr].dis;
	return vec[op][now][nr].dis-vec[op][now][nl].dis;
}
long long solve(rg int now,rg int l,rg int r){
	rg long long nans=cx(0,now,l,r);
	rg int sizfa,siznow;
	for(rg int i=now;newfa[i];i=newfa[i]){
		nans+=cx(0,newfa[i],l,r);
		sizfa=nowsiz;
		nans-=cx(1,i,l,r);
		siznow=nowsiz;
		nans+=1LL*(sizfa-siznow)*getdis(newfa[i],now);
	}
	return nans;
}
int main(){
	memset(h,-1,sizeof(h));
	n=read(),q=read(),maxage=read();
	for(rg int i=1;i<=n;i++) x[i]=read();
	rg int aa,bb,cc,l,r;
	for(rg int i=1;i<n;i++){
		aa=read(),bb=read(),cc=read();
		ad(aa,bb,cc);
		ad(bb,aa,cc);
	}
	dfs1(1,0);
	dfs2(1,1);
	maxsiz[0]=0x3f3f3f3f,rt=0,totsiz=n;
	getroot(1,0);
	predfs(rt);
	for(rg int i=1;i<=n;i++) updat(i);
	for(rg int i=1;i<=n;i++){
		std::sort(vec[0][i].begin(),vec[0][i].end());
		std::sort(vec[1][i].begin(),vec[1][i].end());
		for(rg int j=1;j<vec[0][i].size();j++) vec[0][i][j].dis+=vec[0][i][j-1].dis;
		for(rg int j=1;j<vec[1][i].size();j++) vec[1][i][j].dis+=vec[1][i][j-1].dis;
	}
	for(rg int i=1;i<=q;i++){
		aa=read(),bb=read(),cc=read();
		l=std::min((bb+latans)%maxage,(cc+latans)%maxage);
		r=std::max((bb+latans)%maxage,(cc+latans)%maxage);
		latans=solve(aa,l,r);
		printf("%lld\n",latans);
	}
	return 0;
}

P3345 [ZJOI2015]幻想鄉戰略遊戲

題目傳送門

分析

這道題實際上就是讓我們尋找帶權重心

假設當前的點為 \(x\)\(x\) 的子節點為 \(u\),\(u\)\(x\) 之間的邊權為 \(len\)

如果 \(x\) 子樹內一點 \(u\) 更優,那麼一定有 \((totsiz-siz[u]) \times len - siz[u] \times len <0\)

因為邊權是正的,所以就有 \(2siz[u]>totsiz\)

顯然滿足這個條件的兒子只有一個

因為每一個點的度數不會超過 \(20\)

因此可以在點分樹上暴力地尋找

注意一定要在點分樹上找,不要跑到原樹上去找,否則複雜度就假了

如果用 \(st\)\(O(1)\)\(lca\),那麼複雜度就是 \(20qlog^2n\)

但實際上跑得還沒有樹剖快

程式碼

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<iostream>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=2e5+5;
int h[maxn],tot=1;
struct asd{
	int to,nxt,val;
}b[maxn<<1];
void ad(rg int aa,rg int bb,rg int cc){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	b[tot].val=cc;
	h[aa]=tot++;
}
int n,q,x[maxn],siz[maxn],eul[maxn],dfnc,dep[maxn],lg[maxn<<1],st[maxn<<1][22],rk[maxn<<1];
long long dis[maxn];
void dfs(rg int now,rg int lat){
	dep[now]=dep[lat]+1;
	eul[now]=++dfnc;
	rk[dfnc]=now;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		dis[u]=dis[now]+b[i].val;
		dfs(u,now);
		rk[++dfnc]=now;
	}
}
void prest(){
	for(rg int i=2;i<=dfnc;i++) lg[i]=lg[i>>1]+1;
	for(rg int i=1;i<=dfnc;i++) st[i][0]=rk[i];
	for(rg int j=1;j<=20;j++){
		for(rg int i=1;i+(1<<j)-1<=dfnc;i++){
			if(dep[st[i][j-1]]<dep[st[i+(1<<(j-1))][j-1]]) st[i][j]=st[i][j-1];
			else st[i][j]=st[i+(1<<(j-1))][j-1];
		}
	}
}
int getlca(rg int xx,rg int yy){
	xx=eul[xx],yy=eul[yy];
	if(xx>yy) std::swap(xx,yy);
	rg int nk=lg[yy-xx+1];
	return dep[st[xx][nk]]<dep[st[yy-(1<<nk)+1][nk]]?st[xx][nk]:st[yy-(1<<nk)+1][nk];
}
long long getdis(rg int xx,rg int yy){
	return dis[xx]+dis[yy]-2LL*dis[getlca(xx,yy)];
}
int maxsiz[maxn],totsiz,rt;
bool vis[maxn];
void getroot(rg int now,rg int lat){
	maxsiz[now]=0,siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat || vis[u]) continue;
		getroot(u,now);
		siz[now]+=siz[u];
		maxsiz[now]=std::max(maxsiz[now],siz[u]);
	}
	maxsiz[now]=std::max(maxsiz[now],totsiz-siz[now]);
	if(maxsiz[rt]>maxsiz[now]) rt=now;
}
void getsiz(rg int now,rg int lat){
	siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat || vis[u]) continue;
		getsiz(u,now);
		siz[now]+=siz[u];
	}
}
int newfa[maxn];
std::vector<std::pair<int,int> > g[maxn];
void predfs(rg int now){
	vis[now]=1;
	getsiz(now,0);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(!vis[u]){
			rt=0,totsiz=siz[u];
			getroot(u,now);
			newfa[rt]=now;
			g[now].push_back(std::make_pair(u,rt));
			predfs(rt);
		}
	}
}
long long sum1[maxn],sum2[maxn];
int siz1[maxn],siz2[maxn];
void updat(rg int now,rg int val){
	for(rg int i=now;i;i=newfa[i]){
		sum1[i]+=1LL*getdis(now,i)*val;
		siz1[i]+=val;
	}
	for(rg int i=now;newfa[i];i=newfa[i]){
		sum2[i]+=1LL*getdis(now,newfa[i])*val;
		siz2[i]+=val;
	}
}
long long cx(rg int now){
	rg long long nans=sum1[now];
	for(rg int i=now;newfa[i];i=newfa[i]){
		nans+=sum1[newfa[i]];
		nans-=sum2[i];
		nans+=1LL*getdis(newfa[i],now)*(siz1[newfa[i]]-siz2[i]);
	}
	return nans;
}
long long solve(rg int now){
	rg long long tmp=cx(now);
	for(rg int i=0;i<g[now].size();i++){
		if(cx(g[now][i].first)<tmp) return solve(g[now][i].second);
	}
	return tmp;
}
int main(){
	memset(h,-1,sizeof(h));
	n=read(),q=read();
	rg int aa,bb,cc;
	for(rg int i=1;i<n;i++){
		aa=read(),bb=read(),cc=read();
		ad(aa,bb,cc);
		ad(bb,aa,cc);
	}
	dfs(1,0);
	prest();
	maxsiz[0]=0x3f3f3f3f,rt=0,totsiz=n;
	getroot(1,0);
	predfs(rt);
	memset(vis,0,sizeof(vis));
	rt=0,totsiz=n;
	getroot(1,0);
	for(rg int i=1;i<=q;i++){
		aa=read(),bb=read();
		updat(aa,bb);
		printf("%lld\n",solve(rt));
	}
	return 0;
}

P2056 [ZJOI2007]捉迷藏

題目傳送門

分析

和其它點分樹的題目一樣,對於每一個節點我們開兩個堆,防止同一個子樹內的節點對父親節點做多次貢獻

一個堆裡存點分樹中 \(u\) 的子樹所有點到點分樹中 \(u\) 的父親的距離

另一個堆裡存點分樹中 \(u\) 的所有兒子對 \(u\) 貢獻的最大值

那麼答案就是第二個堆中最大值與次大值之和

加入貢獻時,我們直接扔到大根堆裡就可以了

刪除貢獻時,我們再開一個堆,堆裡面儲存所有已經刪除過的元素

查詢時只要不斷彈兩個堆的堆頂,直到一個堆為空或者兩個堆堆頂的元素不相同為止

程式碼

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<iostream>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e5+5;
int h[maxn],tot=1;
struct asd{
	int to,nxt;
}b[maxn<<1];
void ad(rg int aa,rg int bb){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	h[aa]=tot++;
}
int n,q,siz[maxn],fa[maxn],dep[maxn],son[maxn];
void dfs1(rg int now,rg int lat){
	fa[now]=lat;
	dep[now]=dep[lat]+1;
	siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		dfs1(u,now);
		siz[now]+=siz[u];
		if(siz[son[now]]<siz[u]) son[now]=u;
	}
}
int tp[maxn];
void dfs2(rg int now,rg int top){
	tp[now]=top;
	if(son[now]) dfs2(son[now],top);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==son[now] || u==fa[now]) continue;
		dfs2(u,u);
	}
}
int getlca(rg int xx,rg int yy){
	while(tp[xx]!=tp[yy]){
		if(dep[tp[xx]]<dep[tp[yy]]) std::swap(xx,yy);
		xx=fa[tp[xx]];
	}
	return dep[xx]<dep[yy]?xx:yy;
}
int getdis(rg int xx,rg int yy){
	return dep[xx]+dep[yy]-2*dep[getlca(xx,yy)];
}
int maxsiz[maxn],totsiz,rt;
bool vis[maxn];
void getroot(rg int now,rg int lat){
	maxsiz[now]=0,siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat || vis[u]) continue;
		getroot(u,now);
		siz[now]+=siz[u];
		maxsiz[now]=std::max(maxsiz[now],siz[u]);
	}
	maxsiz[now]=std::max(maxsiz[now],totsiz-siz[now]);
	if(maxsiz[rt]>maxsiz[now]) rt=now;
}
void getsiz(rg int now,rg int lat){
	siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat || vis[u]) continue;
		getsiz(u,now);
		siz[now]+=siz[u];
	}
}
int newfa[maxn];
void predfs(rg int now){
	vis[now]=1;
	getsiz(now,0);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(!vis[u]){
			rt=0,totsiz=siz[u];
			getroot(u,now);
			newfa[rt]=now;
			predfs(rt);
		}
	}
}
char s[maxn];
struct Splay{
	std::priority_queue<int> q1;
	std::priority_queue<int> q2;
	void updat(){
		for(;!q1.empty() && !q2.empty() && q1.top()==q2.top();){
		    q1.pop();
		    q2.pop();
		}
	}
	void insert(rg int val){
		q1.push(val);
	}
	void pop(rg int val){
		q2.push(val);
	}
	int getmax(){
		updat();
		return q1.top();
	}
	int getsec(){
		updat();
		rg int now1=q1.top();q1.pop();
		updat();
		rg int now2=q1.top();q1.pop();
		q1.push(now1),q1.push(now2);
		return now2;
	}
	int size(){
		return q1.size()-q2.size();
	}
}ansnow[maxn],ansfa[maxn],ans;
void updat(rg int now){
	vis[now]^=1;
	if(vis[now]==0){
		if(ansnow[now].size()>=2) ans.pop(ansnow[now].getmax()+ansnow[now].getsec());
		ansnow[now].insert(0);
		if(ansnow[now].size()>=2) ans.insert(ansnow[now].getmax()+ansnow[now].getsec());
		for(rg int i=now;newfa[i];i=newfa[i]){
			if(ansnow[newfa[i]].size()>=2) ans.pop(ansnow[newfa[i]].getmax()+ansnow[newfa[i]].getsec());
			rg int ndis=getdis(now,newfa[i]);
			if(ansfa[i].size()) ansnow[newfa[i]].pop(ansfa[i].getmax());
			ansfa[i].insert(ndis);
			if(ansfa[i].size()) ansnow[newfa[i]].insert(ansfa[i].getmax());
			if(ansnow[newfa[i]].size()>=2) ans.insert(ansnow[newfa[i]].getmax()+ansnow[newfa[i]].getsec());
		}
	} else {
		if(ansnow[now].size()>=2) ans.pop(ansnow[now].getmax()+ansnow[now].getsec());
		ansnow[now].pop(0);
		if(ansnow[now].size()>=2) ans.insert(ansnow[now].getmax()+ansnow[now].getsec());
		for(rg int i=now;newfa[i];i=newfa[i]){
			if(ansnow[newfa[i]].size()>=2) ans.pop(ansnow[newfa[i]].getmax()+ansnow[newfa[i]].getsec());
			rg int ndis=getdis(now,newfa[i]);
			if(ansfa[i].size()) ansnow[newfa[i]].pop(ansfa[i].getmax());
			ansfa[i].pop(ndis);
			if(ansfa[i].size()) ansnow[newfa[i]].insert(ansfa[i].getmax());
			if(ansnow[newfa[i]].size()>=2) ans.insert(ansnow[newfa[i]].getmax()+ansnow[newfa[i]].getsec());
		}
	}
}
int cnt;
int main(){
	memset(h,-1,sizeof(h));
	n=read();
	rg int aa,bb;
	for(rg int i=1;i<n;i++){
		aa=read(),bb=read();
		ad(aa,bb);
		ad(bb,aa);
	}
	dfs1(1,0);
	dfs2(1,1);
	maxsiz[0]=0x3f3f3f3f,rt=0,totsiz=n;
	getroot(1,0);
	predfs(rt);
	q=read();
	memset(vis,0,sizeof(vis));
	for(rg int i=1;i<=n;i++){
		for(rg int j=i;newfa[j];j=newfa[j]){
			ansfa[j].insert(getdis(i,newfa[j]));
		}
	}
	for(rg int i=1;i<=n;i++){
		ansnow[i].insert(0);
		if(newfa[i] && ansfa[i].size()) ansnow[newfa[i]].insert(ansfa[i].getmax());
	}
	for(rg int i=1;i<=n;i++){
		if(ansnow[i].size()>=2) ans.insert(ansnow[i].getmax()+ansnow[i].getsec());
	}
	cnt=n;
	for(rg int i=1;i<=q;i++){
		scanf("%s",s+1);
		if(s[1]=='G'){
			if(cnt==0) printf("-1\n");
			else if(cnt==1) printf("0\n");
			else printf("%d\n",ans.getmax());
		} else {
			aa=read();
			if(vis[aa]) cnt++;
			else cnt--;
			updat(aa);
		}
	}
	return 0;
}

相關文章