【树】全局平衡二叉树

全局平衡二叉树

学习资料:博客1博客2

注:博客1讲得比较详细,博客2的代码和博客1区别。

时间复杂度:(mathcal{O(nlogn)})

但是 常数比较大(但也没有LCT那么大)!虽然树链剖分的时间复杂度是 (mathcal{O(n logn^2)}),但是树链剖分有时会更快。复杂度允许下,能用树剖还是用树剖好。

作用:

全局平衡二叉树是静态树,即区别于LCT,建成后,树的形态不变。同树链剖分,可用于树的修改和查询。

构造:

跟树剖类似,求出树的重儿子(重链)。

对于每一根重链(一根,即 从链头到链尾),建一棵平衡二叉树,然后将链头节点的父亲用作二叉树的根父亲。对于每一根轻链,直接保存其父亲为原始父亲。

在构建平衡二叉树时,以结点的轻儿子子树总和+1的大小作为权重,亦即,以 (siz[u]-siz[son[u]]) 作为节点 (u) 的权重。

构建时需保留在新树中

  • 所有节点的父亲: (tfa[u])
  • 重节点的左右儿子:(ch[u][0])(ch[u][1])
  • 根据需要,保留节点深度:(dep[u])
  • 根据需要,保留当前节点子树的信息:(val[u]/sum[u]/mi[u]...)

过程:

  • 处理出 (lsiz)(son)

    int siz[maxn],lsiz[maxn],son[maxn];
    void dfs(int u,int f)
    {
        siz[u]=1;
        for(int i=head[u];i;i=nxt[i])
            if(to[i]!=f){
                dfs(to[i],u);
                siz[u]+=siz[to[i]];
                if(siz[to[i]]>siz[son[u]])son[u]=to[i];
            }
        lsiz[u]=siz[u]-son[u];
    }//处理出lsiz和son
    
  • 建树

    int tfa[maxn],ch[maxn][2],dep[maxn];
    int si[maxn],vsi[maxn],tot;//si存重链节点序列 vsi是重链节点序列权值(即lsiz)前缀和
    bool vis[maxn];
    int update(int u){}//更新节点信息 比如 mi[u]=min(val[u],mi[ch[u][]]);
    int pushup(int u){
        if(!u)return;
        if(getson(u)!=-1)update(u);// 节点u是其父亲的左儿子或右儿子
    }
    int sbuild(int l,int r,int d)//先看build 再看sbuild
    {
        if(l>r)return 0;
        int mid=lower_bound(vsi+l,vsi+r+1,vsi[r]+vsi[l]>>1)-vsi;
        int u=si[mid];dep[u]=d;
        if(ch[u][0]=sbuild(1,mid-1,d+1))tfa[ch[u][0]]=u;
        if(ch[u][1]=sbuild(mid+1,r,d+1))tfa[ch[u][1]]=u;
        update(u);
    }
    int build(int u,int d)
    {
        int pos;
        for(pos=u,tot=0;pos;pot=son[pos]){
            si[++tot]=pos;vsi[tot]=vsi[tot-1]+lsiz[pos];vis[pos]=1;
        }//当前点必是重链头节点 然后保存重链序列
        int rt=sbuild(1,tot,d);//对当前重链建平衡树
        for(pos=u;pos;pos=son[u])
            for(int i=head[pos];i;i=nxt[i])
                if(!vis[to[i]])//非重儿子 即 新的重链头
                    tfa[build(to[i],dep[pos]+1)]=pos;//当前节点作为 建成的子平衡树的根 的父亲
    }
    
  • 查询

    找到的几个全局平衡树的博客都是luogu动态dp的这个例题,即 查询是对子树的查询,对子树的查询,在建树和更新过程中重要保存好子树信息,查询时直接输出即可。

    而对于链的查询,比如查询链 ((u,v)) 的点权和,或者链 ((u,v)) 的点权最值,则需要 (log)

    找不到相关博客,想到的方法或许不是最优。具体如下:

    • 需要先用 (st) 表 预处理原树(lca) ,用来 (mathcal{O(1)}) 计算两点间距离。设 (pu=u,pv=v)

    • 对于 (u,v) ,同树剖一样,每次处理都是处理 (dep) 较大的节点,然后计算信息后向上处理。

    • 对于当前节点为 (u)(u) 在链上,判断 (tfa[u]) 是否在 链 ((pu,pv)) 上,且设

      [x=(ch[tfa[u]][1]==u)?1:(ch[tfa[u]][0]==u?0:-1) ]

      • (tfa[u]) 在链上,则 (tfa[u])(u) 可能在同一棵或不同一棵平衡树上,即 (x) 是否等于 -1​。

        假如 (x==-1),则需计算 (ch[u][0]) 的子树信息。否则 计算 (ch[u][x)^(1]) 的子树信息。

        再计算 (tfa[u]) 的节点信息

      • (tfa[u]) 不在链上,则 (tfa[u])(u) 必定在同一棵平衡二叉树上。只需计算 (ch[u][x]) 的子树信息。

    • (u=tfa[u])

    具体代码如下:

    inline bool getson(int x){
        return (ch[tfa[x]][1]==x)?1:((ch[tfa[x]][0]==x)?0:-1);
    }
    inline bool online(int u,int v,int po){
        returndis(u,po)+dis(v,po)==dis(u,v);
    }
    //比如计算 链u v之间的最小点权
    int cal(int u,int v)
    {
        int x,pu=u,pv=v;bool juu=1,juv=1,juf;
        int res=min(val[u],val[v]);
        while(u!=v)
        {
            if(dep[u]<dep[v])swap(u,v),swap(juu,juv);
            x=getson(u);
            juf=online(pu,pv,tfa[u]);
            if(!juf&&juu){
                if(ch[u][x])res=min(res,mi[ch[u][x]]);
            }else if(juf&&juu){
                if(x==-1)res=min(res,mi[ch[u][0]]);
                else res=min(res,mi[ch[u][x^1]]);
                res=min(res,val[tfa[u]])
            }
            u=tfa[u];juu=juf;
        }
        return res;
    }
    


一道例题的代码(虽然因为常数过大而超时,树剖反而不超时

hdu6765(2020多校2C)

//#pragma GCC optimize(2)
#include<algorithm>
#include<cstdio>
#include<ctime>
#define min(a,b) (a)<(b)?(a):(b) 
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=5e5+5;

char buf[1<<20],*P1=buf,*P2=buf;
//#define gc() (P1==P2&&(P2=(P1=buf)+fread(buf,1,1<<20,stdin),P1==P2)?EOF:*P1++)
#define gc() getchar()
#define TT template<class T>inline
TT void read(T&x){
    x=0;register char c=gc();register bool f=0;
    while(c<48||c>57){f^=c=='-',c=gc();}
    while(47<c&&c<58)x=(x<<3)+(x<<1)+(c^48),c=gc();
    if(f)x=-x;
}

int to[maxn<<1],nxt[maxn<<1];
int head[maxn],ecnt;
inline void addedge(int u,int v)
{
    to[++ecnt]=v;nxt[ecnt]=head[u];
    head[u]=ecnt;
}
int depth[maxn<<1],id[maxn],rid[maxn<<1],cnt,st[maxn<<1][25];
void dfs(int u,int f,int d)
{
    id[u]=++cnt;rid[cnt]=u;depth[cnt]=d;
    for(int i=head[u];i;i=nxt[i])
        if(to[i]!=f){
            dfs(to[i],u,d+1);
            rid[++cnt]=u;depth[cnt]=d;
        }
}
int lg[maxn<<1];
void init()
{
    lg[0]=-1;
    for(int i=1;i<=cnt;i++)lg[i]=lg[i>>1]+1;
    for(int i=1;i<=cnt;i++)st[i][0]=i;
    for(int j=1;(1<<j)<=cnt;j++)
        for(int i=1;i+(1<<j)-1<=cnt;i++)
            st[i][j]=depth[st[i][j-1]]<depth[st[i+(1<<j-1)][j-1]]?
                     st[i][j-1]:
                     st[i+(1<<j-1)][j-1];
}
inline int lca(int u,int v)
{
    if(id[u]>id[v])swap(u,v);
    int s=id[u],t=id[v],len=lg[t-s+1];
    return depth[st[s][len]]<depth[st[t-(1<<len)+1][len]]?rid[st[s][len]]:rid[st[t-(1<<len)+1][len]];
}
inline int dis(int u,int v){
    return depth[id[u]]+depth[id[v]]-(depth[id[lca(u,v)]]<<1);
}

//gbbt
int siz[maxn],lsiz[maxn],son[maxn];
void dfs1(int u,int f)//处理son和lsiz 的信息
{
    siz[u]=1;
    for(int i=head[u];i;i=nxt[i])
        if(to[i]!=f){
            dfs1(to[i],u);
            siz[u]+=siz[to[i]];
            if(siz[to[i]]>siz[son[u]])son[u]=to[i];
        }
    lsiz[u]=siz[u]-siz[son[u]];
}
int col[maxn];
int tfa[maxn],ch[maxn][2];
const int N=30;
unsigned int mi[maxn][N],val[maxn][N];
inline void update(int u){
    for(int i=0;i<N;i++){
        mi[u][i]=val[col[u]][i];
        if(ch[u][0])mi[u][i]=min(mi[u][i],mi[ch[u][0]][i]);
        if(ch[u][1])mi[u][i]=min(mi[u][i],mi[ch[u][1]][i]);
    }
}
inline int getson(int x){return (ch[tfa[x]][1]==x)?1:((ch[tfa[x]][0]==x)?0:-1);}
void pushup(int u){ 
    update(u);
    if(getson(u)!=-1)pushup(tfa[u]);
}
int si[maxn],tot;ll vsi[maxn];
int dep[maxn];bool vis[maxn];
int sbuild(int l,int r,int d)
{
    if(l>r)return 0;
    int mid=lower_bound(vsi+l,vsi+r+1,vsi[r]+vsi[l-1]>>1)-vsi;
    int u=si[mid];dep[u]=d;
    if(ch[u][0]=sbuild(l,mid-1,d+1))tfa[ch[u][0]]=u;
    if(ch[u][1]=sbuild(mid+1,r,d+1))tfa[ch[u][1]]=u;
    update(u);
    return u;
}
int build(int u,int d)
{
    int pos;
    for(pos=u,tot=0;pos;pos=son[pos]){
        si[++tot]=pos;vsi[tot]=vsi[tot-1]+lsiz[pos];
        vis[pos]=1;
    }
    int rt=sbuild(1,tot,d);
    for(pos=u;pos;pos=son[pos])
        for(int i=head[pos];i;i=nxt[i])
            if(!vis[to[i]])
                tfa[build(to[i],dep[pos]+1)]=pos;
    return rt;
}
inline bool online(int u,int v,int po){
    return dis(u,po)+dis(v,po)==dis(u,v);
}
unsigned int tmp[N];
ll cal(int u,int v)
{
    int x,pu=u,pv=v;bool juu=1,juv=1,fju;
    for(int i=0;i<N;i++)tmp[i]=min(val[col[u]][i],val[col[v]][i]);
    while(u!=v)
    {
        if(dep[u]<dep[v])swap(u,v),swap(juu,juv);//dep u > dep v
        x=getson(u);
        fju=online(pu,pv,tfa[u]);
        if(!fju){
            if(juu&&ch[u][x]){
                for(int i=0;i<N;i++)
                    tmp[i]=min(tmp[i],mi[ch[u][x]][i]);
            }
        }else{
            if(juu&&x==-1&&ch[u][0]){
                for(int i=0;i<N;i++)
                    tmp[i]=min(tmp[i],mi[ch[u][0]][i]);
            }
            else if(juu&&x!=-1&&ch[u][x^1]){
                x^=1;
                for(int i=0;i<N;i++)
                    tmp[i]=min(tmp[i],mi[ch[u][x]][i]);
            }
            for(int i=0;i<N;i++)
                tmp[i]=min(tmp[i],val[col[tfa[u]]][i]);
        }
        juu=fju;u=tfa[u];
    }
    ll sum=0;
    for(int i=0;i<N;i++)sum+=tmp[i];
    return sum;
}
mt19937 mt(time(0));
int main()
{
//    freopen("C:\Users\admin\Desktop\tmp\1003.in","r",stdin);
//    freopen("C:\Users\admin\Desktop\tmp\1003out.txt","w",stdout);
    for(int i=0;i<maxn;i++)
        for(int j=0;j<N;j++)val[i][j]=mt();
    int T;
    read(T);
    while(T--)
    {
        int n,m,u,v,rt,lans=0;
        read(n);read(m);
        for(int i=tot=ecnt=cnt=0;i<=n;i++)
            vis[i]=vsi[i]=head[i]=dep[i]=tfa[i]=son[i]=0;
        for(int i=1;i<=n;i++)read(col[i]);
        for(int i=1;i<n;i++)
        {
            read(u);read(v);
            addedge(u,v);addedge(v,u);
        }
        dfs(1,0,0);init();dfs1(1,0);build(1,1);
        int op,x,y;ll sum1,sum2;
        while(m--)
        {
            read(op);read(x);read(y);
            if(op==1){
                x^=lans;y^=lans;
                col[x]=y;pushup(x);
            }else if(op==2){
                read(u);read(v);
                x^=lans;y^=lans;u^=lans;v^=lans;
                sum1=cal(x,y);sum2=cal(u,v);
                if(sum1<sum2)puts("Yes"),lans++;
                else puts("No");
            }
        }
    }
}
原文地址:https://www.cnblogs.com/kkkek/p/13455394.html