bzoj4539/洛谷P3248/loj2050 树 倍增lca+主席树

我们要完成两个操作:插入和询问。
并且大树和模板树两块都要资磁倍增lca和获取两点间距离,这个很好实现。

插入
1.每一次从模板树上搞点到大树上,看作添加一个大点 (就是一棵子树)。我们要搞出一个b1,表示大点的根节点在模板树上的编号,一个b2,表示根节点在大树上的编号,一个ff,表示根节点的父亲在大树上的编号。
2.在大树上连边,边权是两个大点的根节点之间的距离。
在计算这个距离的时候,我们需要将当前添加的大点的父亲解码。首先在询问中二分,就可以知道这个大点在模板树上的根节点编号,然后利用主席树,查询dfs序区间第k大来获取其在模板树上的编号即可。

询问:
每次询问两个点u和v,假设它们的lca是o。我们先找到它们所处的大点x,y,计算:
1.u和v到其所在大点根节点的距离。
2.大树上x到y之间的距离,即u所在大点的根节点到o所在节点的根节点再到v所在节点的根节点之间的距离。
这样的话,我们多算了一段,即假设u进入o所在大点是通过点p,v则是通过点q,那么可以通过p和q算出真实的lca,o,然后用1和2算出的距离减去两倍o所在大点的根到o的距离。

实现略有点麻烦,建议写几个函数做一次调试。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL read() {
    LL q=0;char ch=' ';
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+(LL)(ch-'0'),ch=getchar();
    return q;
}
const int N=100010;
#define RI register int
int n,m,Q,now;
int pos[N],repos[N];

struct orzxzy{//原树
    int tot,h[N],ne[N<<1],to[N<<1],f[N][17],sz[N],dep[N];
    void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;}
    void dfs(int x,int las) {
        f[x][0]=las,sz[x]=1,dep[x]=dep[las]+1,pos[x]=++now,repos[now]=x;
        for(RI i=1;i<=16;++i) f[x][i]=f[f[x][i-1]][i-1];
        for(RI i=h[x];i;i=ne[i])
            if(to[i]!=las) dfs(to[i],x),sz[x]+=sz[to[i]];
    }
    int getdis(int x,int y) {return dep[y]-dep[x];}
    int lca(int x,int y) {
        if(dep[x]<dep[y]) swap(x,y);
        for(RI i=16;i>=0;--i) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
        if(x==y) return x;
        for(RI i=16;i>=0;--i)
            if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        return f[x][0];
    }
}T1;

struct orzabs{//主席树
    int SZ,rt[N];
    struct node{int ls,rs,sum;}tr[N*22];
    void ins(int y,int &x,int l,int r,int mb) {
        x=++SZ,tr[x]=tr[y],++tr[x].sum;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(mb<=mid) ins(tr[y].ls,tr[x].ls,l,mid,mb);
        else ins(tr[y].rs,tr[x].rs,mid+1,r,mb);
    }
    int kth(int x,int y,int l,int r,int mb) {
        if(l==r) return l;
        int mid=(l+r)>>1,kl=tr[tr[y].ls].sum-tr[tr[x].ls].sum;
        if(kl>=mb) return kth(tr[x].ls,tr[y].ls,l,mid,mb);
        else return kth(tr[x].rs,tr[y].rs,mid+1,r,mb-kl);
    }
    void build() {for(RI i=1;i<=n;++i) ins(rt[i-1],rt[i],1,n,repos[i]);}
}T2;

struct orzjyf{//大树
    int kn,tot;
    int h[N],ne[N<<1],to[N<<1],f[N][17],dep[N];LL w[N<<1],dis[N];
    struct node{int b1;LL b2,ff;}tr[N];
    struct orzwy{//记录每一个操作
        int id;LL js;
        friend bool operator < (orzwy a,orzwy b) {return a.js<b.js;}
    }cz[N];
    void getbh(LL x,int &bn,int &me) {//获取一个节点在模板树上的根节点编号和实际编号
        bn=lower_bound(cz+1,cz+1+kn,(orzwy){0,x})-cz;
        int rx=T2.rt[pos[tr[bn].b1]-1],ry=T2.rt[pos[tr[bn].b1]+T1.sz[tr[bn].b1]-1];
        me=T2.kth(rx,ry,1,n,x-cz[bn-1].js);
    }
    void add(int x,int y,LL z) {to[++tot]=y,ne[tot]=h[x],h[x]=tot,w[tot]=z;}
    void newnode(int x,LL fa) {//在大树上添加节点
        ++kn; int bn,me;
        tr[kn]=(node){x,tr[kn-1].b2+(LL)T1.sz[tr[kn-1].b1]+1,fa};
        cz[kn]=(orzwy){n,cz[kn-1].js+T1.sz[x]};
        if(fa) getbh(fa,bn,me),add(bn,kn,T1.getdis(tr[bn].b1,me)+1);
    }
    void dfs(int x,int las) {
        f[x][0]=las,dep[x]=dep[las]+1;
        for(RI i=1;i<=16;++i) f[x][i]=f[f[x][i-1]][i-1];
        for(RI i=h[x];i;i=ne[i])
            if(to[i]!=las) dis[to[i]]=dis[x]+w[i],dfs(to[i],x);
    }
    int lca(int x,int y,int &kx,int &ky) {
        int flag=0;
        if(x==y) return x;
        if(dep[x]<dep[y]) flag=1,swap(x,y);
        for(RI i=16;i>=0;--i) if(dep[f[x][i]]>dep[y]) x=f[x][i];
        kx=x;if(dep[x]>dep[y]) x=f[x][0];
        if(x==y) {if(flag) swap(kx,ky); return y;}
        for(RI i=16;i>=0;--i)
            if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        kx=x,ky=y;if(flag) swap(kx,ky);
        return f[x][0];
    }
}T3;

LL getans(LL x,LL y) {
    int bnx,mex,bny,mey,kx=0,ky=0;LL re;
    T3.getbh(x,bnx,mex),T3.getbh(y,bny,mey);
    re=T1.getdis(T3.tr[bnx].b1,mex)+T1.getdis(T3.tr[bny].b1,mey);
    re+=T3.dis[bnx]+T3.dis[bny]-2LL*T3.dis[T3.lca(bnx,bny,kx,ky)];
    if(kx) T3.getbh(T3.tr[kx].ff,bnx,mex);
    if(ky) T3.getbh(T3.tr[ky].ff,bny,mey);
    re-=2LL*T1.getdis(T3.tr[bnx].b1,T1.lca(mex,mey));
    return re;
}
int main()
{
    LL x,y;
    n=read(),m=read(),Q=read();
    for(RI i=1;i<n;++i)
        x=read(),y=read(),T1.add(x,y),T1.add(y,x);
    T1.dfs(1,0),T2.build(),T3.newnode(1,0);
    for(RI i=1;i<=m;++i) x=read(),y=read(),T3.newnode(x,y);
    T3.dfs(1,0);
    while(Q--) x=read(),y=read(),printf("%lld\n",getans(x,y));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/litble/article/details/79702116