点分树学习笔记

点分树

点分树借鉴了点分治的思想,将分治过程中具有父子关系的重心连边

形成了一颗高度为 \(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;
}
posted @ 2021-01-16 06:15  liuchanglc  阅读(1301)  评论(0编辑  收藏  举报