当前位置: 首页 > news >正文

全局平衡二叉树

发现自己在大力 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;
}
http://www.wxhsa.cn/company.asp?id=7662

相关文章:

  • Transactional注解的方法里 spring怎么知道我用的是哪个jdbctemplate实例
  • 根据参数查询
  • 关于非侵入式脑机接口面向C端一个应用想法
  • Blelloch并行扫描算法
  • 国产化DevOps生态崛起:Gitee如何赋能企业数字化转型
  • 【IEEE出版】2025年电气、控制与人工智能国际学术会议(ICOECAI 2025)
  • 采购计划 vs 物料需求计划(MRP),采购新手最容易搞混的两份“清单”!
  • P10299 [CCC 2024 S5] Chocolate Bar Partition
  • 实用指南:企业实施数字化转型时常见的挑战
  • 当ARMxy+AI边缘计算落地水泵行业就碰撞出怎样的火花?
  • QN8035 FM芯片驱动开发
  • 再见 Claude Code,我选择了 Codex!真香!!
  • 2025中国DevOps工具生态全景:本土化突围与智能化跃迁
  • 字符串转 python 对象 eval
  • 蛋白多序列比对美化
  • Gitee推出Remote mcp-gitee:云端MCP服务开启智能协作新时代
  • Gitee DevOps平台:驱动中国企业数字化转型的核心引擎
  • 10 类多布局扫描图像数据集:支撑 OCR 精度提升与 VLM 微调,覆盖广告 / 简历 / 论文等场景的计算机视觉训练数据
  • 国产化Excel开发组件Spire.XLS教程:C# 轻松将 DataSet 导出到 Excel
  • Mysql:Docker的Mysql容器加载Levenshtein 距离算法脚本,实现“相似度匹配”
  • 树链剖分
  • 【2025-09-17】慢慢得到
  • Excel处理控件Aspose.Cells教程:如何使用Python在Excel中创建下拉列表
  • STM32的电子钟功能实现
  • kylin V11安装mysql8.0.41(glibc2.28)
  • __cpuid
  • Gitee崛起:国产代码托管平台如何重塑企业研发效能新格局
  • 字节SQL数据库开发手册
  • 完整教程:视频上传以及在线播放
  • C++ STL 常用算法