https://www.luogu.org/problemnew/show/P3248
题解
模拟题意即可,把每次接过去的子树看做一个点,然后这个关系构成了一棵树。
大力倍增即可。
代码
#include<bits/stdc++.h>
#define N 100009
#define ls tr[cnt].l
#define rs tr[cnt].r
using namespace std;
typedef long long ll;
int p[20][N],deep[N],tot,head[N],n,m,q,tott,dep[N],pr[20][N],T[N],size[N],c[N],pre[N];
ll dis[20][N],noww,b[N],nowid;
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
struct node{
int l,r,size;
}tr[N*40];
struct edge{int n,to;}e[N<<1];
inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
inline void ins(int &cnt,int l,int r,int id){
cnt=++tott;
tr[cnt].size++;
if(l==r)return;
int mid=(l+r)>>1;
if(mid>=id)ins(ls,l,mid,id);
else ins(rs,mid+1,r,id);
}
int merge(int u,int v){
if(!u||!v)return u^v;
int cnt=++tott;
tr[cnt].size=tr[u].size+tr[v].size;
ls=merge(tr[u].l,tr[v].l);
rs=merge(tr[u].r,tr[v].r);
return cnt;
}
inline int kth(int &cnt,int l,int r,int k){
int mid=(l+r)>>1;
if(l==r)return l;
if(tr[ls].size<k)return kth(rs,mid+1,r,k-tr[ls].size);
else return kth(ls,l,mid,k);
}
void dfs(int u,int fa){
ins(T[u],1,n,u);
size[u]=1;
for(int i=1;(1<<i)<=deep[u];++i)p[i][u]=p[i-1][p[i-1][u]];
for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
int v=e[i].to;deep[v]=deep[u]+1;p[0][v]=u;
dfs(v,u);
size[u]+=size[v];
T[u]=merge(T[u],T[v]);
}
}
inline int getlca(int u,int v){
if(deep[u]<deep[v])swap(u,v);
for(int i=19;i>=0;--i)if(deep[u]-(1<<i)>=deep[v])u=p[i][u];
if(u==v)return u;
for(int i=19;i>=0;--i)if(p[i][u]!=p[i][v])u=p[i][u],v=p[i][v];
return p[0][u];
}
inline int jump(int x,int xx){
for(int i=19;i>=0;--i)if((1<<i)&xx)x=pr[i][x];
return x;
}
int main(){
n=rd();m=rd();q=rd();
ll u,v;
for(int i=1;i<n;++i){
u=rd();v=rd();
add(u,v);add(v,u);
}
dfs(1,0);
b[++b[0]]=1;noww=n;
c[b[0]]=1;
nowid=1;
while(m--){
u=rd();v=rd();
b[++b[0]]=noww+1;noww+=size[u];
c[b[0]]=u;
int x=upper_bound(b+1,b+b[0]+1,v)-b-1;
int preid=x,preroot=c[x],now=kth(T[preroot],1,n,v-b[x]+1);
//preid 被接的树的编号 preroot 被接的树的根 now被接的节点编号
// cout<<x<<" "<<preroot<<" "<<now<<" "<<preid<<" biu \n";
++nowid;
pr[0][nowid]=preid;dis[0][nowid]=deep[now]-deep[preroot]+1;dep[nowid]=dep[preid]+1;
pre[nowid]=now;
for(int i=1;(1<<i)<=dep[nowid];++i)pr[i][nowid]=pr[i-1][pr[i-1][nowid]],
dis[i][nowid]=dis[i-1][nowid]+dis[i-1][pr[i-1][nowid]];
}
while(q--){
u=rd();v=rd();
int x=upper_bound(b+1,b+b[0]+1,u)-b-1,y=upper_bound(b+1,b+b[0]+1,v)-b-1;
int now1=kth(T[c[x]],1,n,u-b[x]+1),now2=kth(T[c[y]],1,n,v-b[y]+1);
// cout<<x<<" "<<y<<" "<<now1<<" "<<now2<<endl;
ll ans=0;
if(x==y){
ans=deep[now1]+deep[now2]-2*deep[getlca(now1,now2)];
printf("%lld\n",ans);
continue;
}
if(dep[x]<dep[y])swap(x,y),swap(now1,now2);
int bu=dep[x]-dep[y];
if(jump(x,bu)==y){
int xx=bu-1;ans+=deep[now1]-deep[c[x]];
for(int i=19;i>=0;--i)if((1<<i)&xx){
ans+=dis[i][x];x=pr[i][x];
}
ans++;
ans+=deep[now2]+deep[pre[x]]-2*deep[getlca(now2,pre[x])];
}
else{
ans+=deep[now1]-deep[c[x]]+deep[now2]-deep[c[y]];
for(int i=19;i>=0;--i)if(dep[x]-(1<<i)>=dep[y]){
ans+=dis[i][x];x=pr[i][x];
}
for(int i=19;i>=0;--i)if(pr[i][x]!=pr[i][y]){
ans+=dis[i][x]+dis[i][y];x=pr[i][x];y=pr[i][y];
}
int dx=pre[x],dy=pre[y];ans+=2;
// cout<<x<<" "<<y<<" "<<pr[0][x]<<" "<<pr[0][y]<<" ";
ans+=deep[dx]+deep[dy]-2*deep[getlca(dx,dy)];
}
printf("%lld\n",ans);
}
return 0;
}