发现自己在大力 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;
}