全局平衡二叉树

发现自己在大力 DS 这个领域有一些欠缺,所以来补一下。

所谓全局平衡二叉树(GBST)就是 LCT 的静态版本。

我们对树先重剖,然后把每条重链上的点拎出来建一个 BST,满足这个 BST 的中序遍历就是这个重链从上到下遍历得到的序列。然后让这个 BST 的根指向这个点原树上的父亲。

这样此时假如说操作是关于点 \(x\) 到根的路径上的点,那么在新建的树上从点 \(x\) 往上跳,每跳到一棵之前没跳过的 BST,实际上有关的点是在 \(x\) 中序遍历之前的那些点。更进一步,每跳一次,如果 \(x\) 作为一个左儿子跳上去,那么有关的点就是跳上去的点和其左子树里的点。

所以说查询/修改一条根链的复杂度是与树高相关的。我们希望构建一颗树高尽量小的树。考虑这样的方式:对重链上的点构建 BST 时,选取关于轻子树大小 \(+1\) 之和的带权中点作为根,然后递归左右子树建树。

容易发现这样建树的树高是 \(O(\log n)\) 的。无论走一条 BST 边还是非 BST 边,都有子树大小至少为原来的两倍。

下面给出我个人实现的一个模板,使用了标记永久化,用于 P4211。

int fa[N],son[N],siz[N],lsiz[N];
void dfs(int u,int f){
	fa[u]=f,siz[u]=1;
	for(int v:G[u]){
		if(v!=f){
			dfs(v,u);
			siz[u]+=siz[v];
			if(siz[son[u]]<siz[v])
				son[u]=v;
		}
	}
	lsiz[u]=siz[u]-siz[son[u]];
}
struct node{
	int fa,ls,rs,sz;
	int ta,v,s;
	bool isrt;
}t[N];
int build_chain(int l,int r,vector<int> &b){
	if(l>=r)return 0;
	int ssiz=0,csiz=0,mn=INF,idx=0;
	for(int i=l;i<r;i++)ssiz+=lsiz[b[i]];
	for(int i=l;i<r;i++){
		int mx=max(csiz,ssiz-csiz-lsiz[b[i]]);
		if(mx<mn)idx=i,mn=mx;
		csiz+=lsiz[b[i]];
	}
	int p=b[idx];
	t[p].sz=r-l;
	if((t[p].ls=build_chain(l,idx,b))!=0)t[t[p].ls].fa=p;
	if((t[p].rs=build_chain(idx+1,r,b))!=0)t[t[p].rs].fa=p;
	return p;
}
int build(int x){
	vector<int> b;
	while(x)b.pb(x),x=son[x];
	for(int u:b)
		for(int v:G[u])
			if(v!=fa[u]&&v!=son[u])
				t[build(v)].fa=u;
	int rt=build_chain(0,b.size(),b);
	t[rt].isrt=1;
	return rt;
}
void pushup(int p){
	t[p].s=t[t[p].ls].s+t[t[p].rs].s+t[p].sz*t[p].ta;
}
void add(int p){
	bool fl=1;
	while(p){
		if(fl){
			t[p].ta++;
			if(t[p].rs){
				t[t[p].rs].ta--;
				pushup(t[p].rs);
			}
		}
		fl=(p!=(t[t[p].fa].ls));
		pushup(p);
		p=t[p].fa;
	}
}
int query(int p){
	int res=0,sz=0;
	bool fl=1;
	while(p){
		if(fl){
			res+=t[t[p].ls].s;
			sz+=1+t[t[p].ls].sz;
		}
		res+=sz*t[p].ta;
		fl=(p!=t[t[p].fa].ls);
		if(t[p].isrt)sz=0;
		p=t[p].fa;
	}
	return res;
}

GBST 的一个重要用途是维护树上 DDP。

仿照树剖做法,我们维护每一棵 BST 上 \(g\) 的矩阵乘积,跳非 BST 边的时候更新即可。

以下是我个人实现的动态 DP 加强版模板代码。

不要忘记 pushup

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
const int N=1e6+10,INF=0x3f3f3f3f,mod=1e9+7;
struct Mat{
	int a[2][2];
	Mat(){memset(a,0,sizeof(a));}
	inline int *operator[](int x){return a[x-1]-1;}
};
Mat operator*(Mat A,Mat B){
	Mat C;
	C[1][1]=max(A[1][1]+B[1][1],A[1][2]+B[2][1]);
	C[1][2]=max(A[1][1]+B[1][2],A[1][2]+B[2][2]);
	C[2][1]=max(A[2][1]+B[1][1],A[2][2]+B[2][1]);
	C[2][2]=max(A[2][1]+B[1][2],A[2][2]+B[2][2]);
	return C;
}
int n,m,a[N];
vector<int> G[N];
Mat g[N];
int fa[N],siz[N],dep[N],son[N],lsiz[N],f[N][2];
void dfs1(int u,int ft){
	fa[u]=ft,dep[u]=dep[ft]+1,siz[u]=1;
	f[u][1]=a[u];
	for(int v:G[u]){
		if(v!=ft){
			dfs1(v,u);
			f[u][0]+=max(f[v][0],f[v][1]);
			f[u][1]+=f[v][0];
			siz[u]+=siz[v];
			if(siz[son[u]]<siz[v])
				son[u]=v;
		}
	}
	lsiz[u]=siz[u]-siz[son[u]];
}
void dfs2(int u,int ft){
	int g0=0,g1=a[u];
	if(son[u])dfs2(son[u],u);
	for(int v:G[u]){
		if(v!=ft&&v!=son[u]){
			dfs2(v,u);
			g0+=max(f[v][0],f[v][1]);
			g1+=f[v][0];
		}
	}
	g[u][1][1]=g[u][1][2]=g0;
	g[u][2][1]=g1;
	g[u][2][2]=-INF;
}
int rt;
struct node{
	Mat v;
	int fa,ls,rs;
	bool isrt;
}t[N];
void pushup(int p){
	t[p].v=g[p];
	if(t[p].ls)t[p].v=t[t[p].ls].v*t[p].v;
	if(t[p].rs)t[p].v=t[p].v*t[t[p].rs].v;
}
int build_chain(int l,int r,vector<int> &b){
	if(l>=r)return 0;
	int ssiz=0,csiz=0,mn=INF,idx=0;
	for(int i=l;i<r;i++)ssiz+=lsiz[b[i]];
	for(int i=l;i<r;i++){
		int mx=max(csiz,ssiz-csiz-lsiz[b[i]]);
		if(mx<mn)idx=i,mn=mx;
		csiz+=lsiz[b[i]];
	}
	int p=b[idx];
	if((t[p].ls=build_chain(l,idx,b))!=0)t[t[p].ls].fa=p;
	if((t[p].rs=build_chain(idx+1,r,b))!=0)t[t[p].rs].fa=p;
	pushup(p);
	return p;
}
int build(int x){
	vector<int> b;
	while(x)b.pb(x),x=son[x];
	for(int u:b)
		for(int v:G[u])
			if(v!=fa[u]&&v!=son[u])
				t[build(v)].fa=u;
	int rt=build_chain(0,b.size(),b);
	t[rt].isrt=1;
	return rt;
}
void Update(int x,int y){
	g[x][2][1]+=y-a[x];
	a[x]=y;
	while(x){
		if(t[x].isrt){
			int l0=t[x].v[1][1],l1=t[x].v[2][1];
			pushup(x);
			int c0=t[x].v[1][1],c1=t[x].v[2][1];
			Mat &fm=g[t[x].fa];
			fm[1][1]+=max(c0,c1)-max(l0,l1);
			fm[1][2]=fm[1][1];
			fm[2][1]+=c0-l0;
		}else pushup(x);
		x=t[x].fa;
	}
}
int lans;
int Query(){
	return max(t[rt].v[1][1],t[rt].v[2][1]);
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<n;i++){
		int u,v;cin>>u>>v;
		G[u].pb(v),G[v].pb(u);
	}
	dfs1(1,0);
	dfs2(1,0);
	rt=build(1);
	for(int i=1;i<=m;i++){
		int x,y;cin>>x>>y;
		x^=lans;
		Update(x,y);
		cout<<(lans=Query())<<'\n';
	}
	return 0;
}
posted @ 2025-09-18 10:47  Linge_Zzzz  阅读(11)  评论(0)    收藏  举报