bzoj4539: [Hnoi2016]树(主席树+倍增)

传送门
题意:给一棵大树,令一棵模板树与这棵树相同,然后进行 m m m次操作,每次选择模板树中的一个节点 a a a和大树中一个节点 b b b,把 a a a这棵子树接在 b b b上面,节点编号顺序跟 a a a中的编号顺序相同。
最后有 q q q次询问问大树上两点距离。


思路:
真·树套树
把每棵树所成一个点,然后相当于先把两个点跳到一个块中再求它们的 l c a lca lca,可以用主席树维护块中编号第 k k k大来维护块中对应点,实现块于块之间的跳跃可以用倍增

#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
    static char buf[rlen],*ib,*ob;
    (ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
    return ib==ob?-1:*ib++;
}
typedef long long ll;
inline ll read(){
    ll ans=0;
    char ch=gc();
    while(!isdigit(ch))ch=gc();
    while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
    return ans;
}
const int N=1e5+5;
int rt[N],pred[N],Dep[N],num[N],top[N],hson[N],dep[N],fa[N],siz[N],st[N][20],n,m,q,tot=0;
ll dis[N];
vector<int>e[N];
struct Node{int id;ll l,r,pre;}a[N];
namespace sgt{
    #define lc son[p][0]
    #define rc son[p][1]
    #define mid (l+r>>1)
    int siz[N*30],son[N*30][2],tot=0;
    inline void update(int&p,int last,int l,int r,int k){
        siz[p=++tot]=siz[last]+1,lc=son[last][0],rc=son[last][1];
        if(l==r)return;
        k<=mid?update(lc,son[last][0],l,mid,k):update(rc,son[last][1],mid+1,r,k);
    }
    inline int query(int pl,int pr,int l,int r,int k){
        if(l==r)return l;
        int tmp=siz[son[pr][0]]-siz[son[pl][0]];
        return tmp>=k?query(son[pl][0],son[pr][0],l,mid,k):query(son[pl][1],son[pr][1],mid+1,r,k-tmp);
    }
    #undef lc
    #undef rc
    #undef mid
}
void dfs1(int p){
    siz[p]=1;
    for(ri i=0,v;i<e[p].size();++i){
        if((v=e[p][i])==fa[p])continue;
        fa[v]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
        if(siz[v]>siz[hson[p]])hson[p]=v;
    }
}
void dfs2(int p,int tp){
    top[p]=tp,pred[num[p]=++tot]=p;
    if(!hson[p])return;
    dfs2(hson[p],tp);
    for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=hson[p]&&v!=fa[p])dfs2(v,v);
}
inline int lca(int x,int y){
    while(top[x]^top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}
inline int find(ll x,int R){
    int l=1,r=R,ret=R;
    while(l<=r){
        int mid=l+r>>1;
        if(a[mid].r>=x)ret=mid,r=mid-1;
        else l=mid+1;
    }
    return ret;
}
inline ll dist(int blo,int x){return dis[blo]+dep[x]-dep[a[blo].id];}
inline int idx(int blo,ll x){
    int t=a[blo].id;
    return sgt::query(rt[num[t]-1],rt[num[t]+siz[t]-1],1,n,x-a[blo].l+1);
}
inline int Lca(int x,int y){
    if(Dep[x]<Dep[y])swap(x,y);
    for(ri tmp=Dep[x]-Dep[y],i=19;~i;--i)if((tmp>>i)&1)x=st[x][i];
    if(x==y)return x;
    for(ri i=19;~i;--i)if(st[x][i]^st[y][i])x=st[x][i],y=st[y][i];
    return st[x][0];
}
inline int Find(int x,int k){
    for(ri i=19;~i;--i)if((k>>i)&1)x=st[x][i];
    return idx(st[x][0],a[x].pre);
}
inline ll calc(int fx,int fy,int x,int y){
    int t=Lca(fx,fy);
    if(fx==t)swap(fx,fy),swap(x,y);
    if(fy==t){
        ll ret=dist(fx,x);
        ret-=dist(t,x=Find(fx,Dep[fx]-Dep[t]-1));
        return ret+dep[x]+dep[y]-2*dep[lca(x,y)];
    }
    ll ret=dist(fx,x)+dist(fy,y);
    ret-=dist(t,x=Find(fx,Dep[fx]-Dep[t]-1));
    ret-=dist(t,y=Find(fy,Dep[fy]-Dep[t]-1));
    return ret+dep[x]+dep[y]-2*dep[lca(x,y)];
}
int main(){
    n=read(),m=read()+1,q=read();
    for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
    dfs1(1),dfs2(1,1);
    for(ri i=1;i<=n;++i)sgt::update(rt[i],rt[i-1],1,n,pred[i]);
    a[1]=(Node){1,1,n,0};
    ll x,y,Tot=n;
    for(ri i=2;i<=m;++i){
        x=read(),y=read();
        a[i]=(Node){x,Tot+1,Tot+siz[x],y},Tot+=siz[x];
        st[i][0]=find(y,i-1),Dep[i]=Dep[st[i][0]]+1,dis[i]=dis[st[i][0]]+dep[idx(st[i][0],y)]-dep[a[st[i][0]].id]+1;
    }
    for(ri j=1;j<20;++j)for(ri i=1;i<=m;++i)st[i][j]=st[st[i][j-1]][j-1];
    while(q--){
        x=read(),y=read();
        int fx=find(x,m),fy=find(y,m);
        x=idx(fx,x),y=idx(fy,y);
        if(fx^fy)cout<<calc(fx,fy,x,y)<<'\n';
        else cout<<dep[x]+dep[y]-2*dep[lca(x,y)]<<'\n';
    }
}

你可能感兴趣的:(数据结构,#,主席树,#,倍增)