【学习笔记】全局平衡二叉树

全局平衡二叉树

全局平衡二叉树的样子(出自 2007 年的一篇论文)

image.png

全局平衡二叉树可以理解为一个静态的 LCT。首先将这棵树重链剖分,可以得到 \(O(\log n)\) 个重链。然后我们设 \(lsz_i\) 表示该点的所有轻子树大小加一(\(u\) 子树大小减去重儿子子树大小)。然后对于我们每一条重链,我们要建出其 Auxiliary Tree(即 LCT 上的 splay)。这个 Auxiliary Tree 应该是一个 BST,并且其权值为 \(lsz\)。具体建法:

  1. 对于每一个重链的区间 \([l,r]\)\(l\)\(r\) 的祖先):
    1. 暴力找到它的带权中点 \(k\)(设 \(s\)\(lsz\) 的前缀和,则找到位置 \(k\) 满足 \(s_{k-1}\le \frac{s_r+s_{l-1}}{2}\le s_k\)\(k\)),于是 \(k\) 为这个区间的根。
    2. 分别计算左儿子区间 \([l,k-1]\) 和右儿子区间 \([k+1,r]\)。这样我们一定有左儿子为祖先,右儿子为子孙。
  2. 对于每个 Auxiliary Tree 的根 \(r\),它向它的父节点在全局平衡二叉树上的点连一条虚边(Path Parent 边)。

然后就建好了!很简单!

这个全局平衡二叉树的高度是 \(O(\log n)\) 的。意味着有很多事情能单 log 直接做到。

QTREE1 点修链查

考虑每个 \(u\) 维护其 Auxiliary Tree 上的子树最大值 \(mx_u\)

修改操作:从 \(u\) 开始向上爬。如果爬的是 Auxiliary Tree 内部的边,要更新父亲的 \(mx\)。如果爬的是 Parent Path 就不用更新了。复杂度 \(O(\log n)\)

查询操作:

  1. \(u,v\) 开始向上爬。边爬边用这个点的左儿子的 \(mx\) 值和自己的值去更新答案,直到爬到了 \(lca\) 所在重链。
  2. 对于任意一个同重链祖先链询问 \((x,y)\)\(y\)\(x\) 的祖先),相当于在一个 Auxiliary Tree 上做一个(深度)区间询问。Auxiliary Tree 本来就是点有关深度的一个 BST,直接像线段树那样做就行了。

复杂度 \(O(\log n)\)

这玩意儿写起来和树剖没长多少(就是建树稍微长了一点),常数很小,还比树剖少个 log,很有趣。

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef vector<pii> vp;
typedef unsigned long long ull;

long long read() {
    long long res=0, w=1; char c=getchar();
    while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
    while(isdigit(c)) {res=res*10+c-48, c=getchar();}
    return res*w;
}

const int N=1e5+9;
int n,w[N],sz[N],d[N],son[N],id[N],fa[N],top[N],lsz[N];
vector<pair<int,pii> > e[N];
vp t[N];
char op[N];

void dfs1(int u) {
    d[u]=d[fa[u]]+1; sz[u]=1;
    for(auto ed:e[u]) if(ed.fi!=fa[u]) {
        int v=ed.fi; w[v]=ed.se.fi; id[ed.se.se]=v; fa[v]=u;
        dfs1(v); sz[u]+=sz[v]; if(sz[v]>sz[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int tp,int ss) {
    top[u]=tp; lsz[u]=sz[u]-sz[son[u]]; t[tp].emplace_back(pii(u,ss+lsz[u]));
    for(auto v:e[u]) if(v.fi!=son[u]&&v.fi!=fa[u]) dfs2(v.fi,v.fi,0);
    if(son[u]) dfs2(son[u],tp,ss+lsz[u]);
}

namespace GBST {
    int ls[N],rs[N],mx[N],af[N],pf[N],mind[N],maxd[N],rt[N];
    int build(int tp,int l,int r) {
        if(l>r) return 0;
        if(l==r) {
            int x=t[tp][l].fi; mx[x]=w[x], mind[x]=d[x], maxd[x]=d[x];
            return x;
        }
        int sum=(l?t[tp][l-1].se:0)+t[tp][r].se>>1;
        rep(k,l,r) if(t[tp][k].se>sum) {
            int x=t[tp][k].fi;
            af[ls[x]=build(tp,l,k-1)]=x, af[rs[x]=build(tp,k+1,r)]=x;
            mx[x]=max(w[x],max(mx[ls[x]],mx[rs[x]]));
            if(ls[x]) mind[x]=min(d[x],mind[ls[x]]); else mind[x]=d[x];
            if(rs[x]) maxd[x]=max(d[x],maxd[rs[x]]); else maxd[x]=d[x];
            return x;
        }
        return assert(0), -1;
    }
    void init() {
        rep(i,1,n) {
            if(i==top[i]) rt[i]=build(i,0,t[i].size()-1), pf[rt[i]]=fa[i];
        }
    }
    void mdf(int x,int y) {
        w[x]=y, mx[x]=max(max(mx[ls[x]],mx[rs[x]]),w[x]);
        while(x) {
            if(af[x]) x=af[x], mx[x]=max(max(mx[ls[x]],mx[rs[x]]),w[x]);
            else x=pf[x];
        }
    }
    int qry(int u,int l,int r,int res=0) {
        if(l>r) return 0; if(!u) return 0;
        if(l<=d[u]&&d[u]<=r) res=max(res,w[u]);
        res=max(res,d[u]<=r&&mind[u]>=l?mx[ls[u]]:qry(ls[u],l,min(d[u]-1,r)));
        res=max(res,d[u]>=l&&maxd[u]<=r?mx[rs[u]]:qry(rs[u],max(l,d[u]+1),r));
        return res;
    }
    int query(int u,int v,int ans=0) {
        while(top[u]!=top[v]) {
            if(d[top[u]]<d[top[v]]) swap(u,v);
            for(int yu=u;;u=af[u]) {
                if(d[u]<=d[yu]) ans=max(ans,max(w[u],mx[ls[u]]));
                if(!af[u]) break;
            } u=pf[u];
        } if(d[u]<d[v]) swap(u,v);
        return max(ans,qry(rt[top[u]],d[v]+1,d[u]));
    }
}

signed main() {
    n=read();
    rep(i,2,n) {
        int u=read(), v=read(), w=read();
        e[u].emplace_back(make_pair(v,pii(w,i-1)));
        e[v].emplace_back(make_pair(u,pii(w,i-1)));
    }
    dfs1(1); dfs2(1,1,0); GBST::init();
    while(1) {
        scanf("%s",op); int x,y;
        if(op[0]=='D') break;
        else if(op[0]=='C') x=read(), y=read(), GBST::mdf(id[x],y);
        else x=read(), y=read(), printf("%d\n",GBST::query(x,y));
    }
    return 0;
}

几乎没怎么硬卡常,让这个解排到了洛谷最优解首页。如果把前面 Build 所用的 vector 换成其他结构,加一个更好的快读,那么可以再快一些。不过这样卡常就很无趣了。

posted @ 2022-08-11 10:04  LarsWerner  阅读(198)  评论(0编辑  收藏  举报