来跟我一起说:陈指导是魔鬼,把一道我任务清单里躺了两年的题搬出来强制我写了
但说实话可能昨天状态挺好一下子就写过了,没有调成傻逼
但这题的思路其实很简单,真·树套树即可
即每次进行复制操作时,将每次复制的子树看做一个大点,这样可以建立一个新的树,我们称为大树
对于大树上的点我们需要维护一些必要的信息,比如这个某个节点(i)对应的小节点的编号区间(L_i,R_i),在模板树上对应的复制点(pre_i),在大树上悬挂在哪个小节点下面(lst_i)
根据这些我们可以完成一些操作:
getrt(x)
,表示找到小节点(x)对应的大树上的节点是哪个,这个可以通过与(L_i)的大小关系来二分getpre(x)
,表示找到小节点(x)对应的模板树上的节点是哪个,由于每次标号相当于一个求子树第(k)大的过程,我们可以DFS序+主席树,也可以线段树合并
然后接下来的思路就是倍增了,对于大树上的每个节点我们记录倍增的祖先和距离,最后将维护下模板树上的距离关系即可
说起来挺容易的但细节挺多,尤其是倍增求答案的时候有很多种小情况要注意
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
#define int long long
using namespace std;
const int N=100005,P=17;
int n,m,q,x,y;
namespace T1 //Template Tree
{
struct edge
{
int to,nxt;
}e[N<<1]; int head[N],cnt,rt[N],size[N];
inline void addedge(CI x,CI y)
{
e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
e[++cnt]=(edge){x,head[y]}; head[y]=cnt;
}
class Segment_Tree
{
private:
struct segment
{
int ch[2],size;
}node[N*P<<2]; int tot;
#define lc(x) node[x].ch[0]
#define rc(x) node[x].ch[1]
#define S(x) node[x].size
#define TN CI l=1,CI r=n
public:
inline void modify(int& now,CI pos,TN)
{
now=++tot; ++S(now); if (l==r) return; int mid=l+r>>1;
if (pos<=mid) modify(lc(now),pos,l,mid); else modify(rc(now),pos,mid+1,r);
}
inline int merge(CI x,CI y,TN)
{
if (!x||!y) return x|y; int now=++tot,mid=l+r>>1; S(now)=S(x)+S(y);
lc(now)=merge(lc(x),lc(y),l,mid); rc(now)=merge(rc(x),rc(y),mid+1,r); return now;
}
inline int query(CI now,CI k,TN)
{
if (l==r) return l; int mid=l+r>>1; if (k<=S(lc(now)))
return query(lc(now),k,l,mid); else return query(rc(now),k-S(lc(now)),mid+1,r);
}
#undef lc
#undef rc
#undef S
#undef TN
}SEG;
#define to e[i].to
class Tree_Distance_Solver
{
private:
int anc[N][P],dep[N];
inline int getlca(int x,int y)
{
RI i; if (dep[x]<dep[y]) swap(x,y);
for (i=P-1;~i;--i) if (dep[anc[x][i]]>=dep[y]) x=anc[x][i];
if (x==y) return x; for (i=P-1;~i;--i)
if (anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
return anc[x][0];
}
public:
inline void DFS(CI now=1,CI fa=0)
{
RI i; SEG.modify(rt[now],now); size[now]=1;
for (anc[now][0]=fa,i=0;i<P-1;++i)
if (anc[now][i]) anc[now][i+1]=anc[anc[now][i]][i]; else break;
for (dep[now]=dep[fa]+1,i=head[now];i;i=e[i].nxt)
if (to!=fa) DFS(to,now),size[now]+=size[to],
rt[now]=SEG.merge(rt[now],rt[to]);
}
inline int getdis(CI x,CI y)
{
return dep[x]+dep[y]-(dep[getlca(x,y)]<<1LL);
}
}T;
};
namespace T2 //Big Tree
{
int pre[N],lst[N],L[N],R[N],tot,num,anc[N][P],dep[N],dis[N][P];
inline int getrt(CI x)
{
int l=1,r=num,mid,ret; while (l<=r)
if (L[mid=l+r>>1]<=x) ret=mid,l=mid+1; else r=mid-1; return ret;
}
inline int getpre(CI x)
{
int rt=getrt(x); return T1::SEG.query(T1::rt[pre[rt]],x-L[rt]+1);
}
inline void relink(CI x,CI y)
{
int rt=getrt(y); dep[++num]=dep[rt]+1; lst[num]=y; pre[num]=x;
L[num]=tot+1; R[num]=tot+T1::size[x]; tot+=T1::size[x];
anc[num][0]=rt; dis[num][0]=T1::T.getdis(getpre(y),pre[rt])+1;
for (RI i=0;i<P-1;++i) if (anc[num][i])
dis[num][i+1]=dis[num][i]+dis[anc[num][i]][i],
anc[num][i+1]=anc[anc[num][i]][i]; else break;
}
inline int query(int x,int y,int ret=0)
{
int rx=getrt(x),ry=getrt(y); RI i;
if (rx==ry) return T1::T.getdis(getpre(x),getpre(y));
if (dep[rx]<dep[ry]) swap(x,y),swap(rx,ry);
ret+=T1::T.getdis(getpre(x),pre[rx]); x=rx;
for (i=P-1;~i;--i) if (dep[anc[x][i]]>dep[ry]) ret+=dis[x][i],x=anc[x][i];
if (getrt(lst[x])==ry) return ret+1+T1::T.getdis(getpre(lst[x]),getpre(y));
ret+=T1::T.getdis(getpre(y),pre[ry]); y=ry;
if (dep[x]>dep[y]) ret+=dis[x][0],x=anc[x][0];
for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i])
ret+=dis[x][i]+dis[y][i],x=anc[x][i],y=anc[y][i];
return ret+T1::T.getdis(getpre(lst[x]),getpre(lst[y]))+2;
}
};
signed main()
{
//reopen("treeman.in","r",stdin); freopen("treeman.out","w",stdout);
RI i; for (scanf("%lld%lld%lld",&n,&m,&q),i=1;i<n;++i)
scanf("%lld%lld",&x,&y),T1::addedge(x,y);
T2::L[1]=T2::pre[1]=T2::num=1; T2::tot=T2::R[1]=n;
for (T1::T.DFS(),i=1;i<=m;++i) scanf("%lld%lld",&x,&y),T2::relink(x,y);
for (i=1;i<=q;++i) scanf("%lld%lld",&x,&y),printf("%lld
",T2::query(x,y));
return 0;
}