bzoj4539
小A想做一棵很大的树,但是他手上的材料有限,只好用点小技巧了。开始,小A只有一棵结点数为N的树,结点的编号为1,2,…,N,其中结点1为根;我们称这颗树为模板树。小A决定通过这棵模板树来构建一颗大树。构建过程如下:(1)将模板树复制为初始的大树。(2)以下(2.1)(2.2)(2.3)步循环执行M次(2.1)选择两个数字a,b,其中1<=a<=N,1<=b<=当前大树的结点数。(2.2)将模板树中以结点a为根的子树复制一遍,挂到大树中结点b的下方(也就是说,模板树中的结点a为根的子树复制到大树中后,将成为大树中结点b的子树)。(2.3)将新加入大树的结点按照在模板树中编号的顺序重新编号。例如,假设在进行2.2步之前大树有L个结点,模板树中以a为根的子树共有C个结点,那么新加入模板树的C个结点在大树中的编号将是L+1,L+2,…,L+C;大树中这C个结点编号的大小顺序和模板树中对应的C个结点的大小顺序是一致的。下面给出一个实例。假设模板树如下图:
根据第(1)步,初始的大树与模板树是相同的。在(2.1)步,假设选择了a=4,b=3。运行(2.2)和(2.3)后,得到新的大树如下图所示
现在他想问你,树中一些结点对的距离是多少。
第一行三个整数:N,M,Q,以空格隔开,N表示模板树结点数,M表示第(2)中的循环操作的次数,Q 表示询问数量。接下来N-1行,每行两个整数 fr,to,表示模板树中的一条树边。再接下来M行,每行两个整数x,to,表示将模板树中 x 为根的子树复制到大树中成为结点to的子树的一次操作。再接下来Q行,每行两个整数fr,to,表示询问大树中结点 fr和 to之间的距离是多少。
输出Q行,每行一个整数,第 i行是第 i个询问的答案。
5 2 3
1 4
1 3
4 2
4 5
4 3
3 2
6 9
1 8
5 3
6
3
3
经过两次操作后,大树变成了下图所示的形状:
结点6到9之间经过了6条边,所以距离为6;类似地,结点1到8之间经过了3条边;结点5到3之间也经过了3条边。
我们将每次操作的一颗子树看成一个块,可以得到一颗树。我们可以二分得到一个节点属于哪个块,以及它在块中是排第几,那么可以用主席树维护新树和模板树节点的对应关系。
对于一次询问,如果他们在同一块,则直接在模板树中求lca,否则块之间在新树中倍增,块之内在模板树中求就可以了。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100010;
typedef long long ll;
struct node{
int ls,rs,d;
}t[N*20];
struct tree{
struct edge{
int x,nex,data;
}e[N*2];
int f[N][20],dep[N],first[N],size[N],tot;
ll dis[N];
int dfn[N],pos[N],ti;
void add(int x,int y,int d){
e[++tot].x=y;
e[tot].data=d;
e[tot].nex=first[x];
first[x]=tot;
}
void dfs(int x,int y){
dfn[x]=++ti; pos[ti]=x;
f[x][0]=y; dep[x]=dep[y]+1;
for(int i=1;i<=17;i++) f[x][i]=f[f[x][i-1]][i-1];
for(int i=first[x];i;i=e[i].nex)
if(e[i].x!=y){
dis[e[i].x]=dis[x]+e[i].data;
dfs(e[i].x,x);
size[x]+=size[e[i].x];
}
size[x]++;
}
int LCA(int x,int y){
if(dep[x]<dep[y]) std::swap(x,y);
for(int i=17;~i;i--)
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=17;~i;i--)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
int s_lca(int x,int y){
for(int i=17;~i;i--)
if(dep[f[x][i]]>dep[y]) x=f[x][i];
return x;
}
}t1,t2;
int num,n,m,q,tmp1,tmp2,tmp,lca,cnt;
int top[N],st[N],dis[N],rt[N];
ll a[N],s,ans,x,y;
void insert(int &k,int l,int r,int x){
t[++cnt]=t[k]; k=cnt; t[k].d++;
if(l==r) return;
int mid=(l+r)>>1;
if(x<=mid) insert(t[k].ls,l,mid,x);
else insert(t[k].rs,mid+1,r,x);
}
int query(int k1,int k2,int l,int r,int x){
if(l==r) return l;
int tmp=t[t[k1].ls].d-t[t[k2].ls].d;
int mid=(l+r)>>1;
if(tmp>=x) return query(t[k1].ls,t[k2].ls,l,mid,x);
else return query(t[k1].rs,t[k2].rs,mid+1,r,x-tmp);
}
int Find(ll x){
int l=1,r=num+1,mid;
while(r-l>1){
mid=(l+r)>>1;
if(a[mid]>x) r=mid;
else l=mid;
}
return l;
}
int Get(ll x,int tmp){
if(tmp==1) return x;
x=x-a[tmp]+1;
int r=t1.dfn[st[tmp]]+t1.size[st[tmp]]-1;
int l=t1.dfn[st[tmp]]-1;
return query(rt[r],rt[l],1,n,x);
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<n;i++){
scanf("%lld%lld",&x,&y);
t1.add(x,y,1); t1.add(y,x,1);
}
t1.dfs(1,0);
for(int i=1;i<=n;i++){
rt[i]=rt[i-1];
insert(rt[i],1,n,t1.pos[i]);
}
a[++num]=1; s=n; st[num]=1;
for(int i=1;i<=m;i++){
scanf("%lld%lld",&x,&y);
a[++num]=s+1; s+=t1.size[x];
tmp=Find(y); top[num]=Get(y,tmp); st[num]=x;
dis[num]=t1.dis[x];
t2.add(num,tmp,t1.dis[Get(y,tmp)]-dis[tmp]+1);
t2.add(tmp,num,t1.dis[Get(y,tmp)]-dis[tmp]+1);
}
t2.dfs(1,0);
for(int i=1;i<=q;i++){
scanf("%lld%lld",&x,&y);
tmp1=Find(x),tmp2=Find(y);
if(tmp1==tmp2){
ans=t1.dis[Get(x,tmp1)]+t1.dis[Get(y,tmp2)]-2*t1.dis[t1.LCA(Get(x,tmp1),Get(y,tmp2))];
printf("%lld\n",ans);
continue;
}
ans=0;
lca=t2.LCA(tmp1,tmp2);
if(t2.dep[tmp1]<t2.dep[tmp2]) swap(tmp1,tmp2),swap(x,y);
if(lca!=tmp2){
ans+=t1.dis[Get(y,tmp2)]-dis[tmp2]+1,y=t2.s_lca(tmp2,lca);
ans+=t2.dis[tmp2]-t2.dis[y]; y=top[y];
} else y=Get(y,tmp2);
ans+=t1.dis[Get(x,tmp1)]-dis[tmp1]+1,x=t2.s_lca(tmp1,lca);
ans+=t2.dis[tmp1]-t2.dis[x]; x=top[x];
ans+=t1.dis[x]+t1.dis[y]-2*t1.dis[t1.LCA(x,y)];
printf("%lld\n",ans);
}
return 0;
}