树
Description
小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)后,得到新的大树如下图所示
现在他想问你,树中一些结点对的距离是多少。
Input
第一行三个整数:N,M,Q,以空格隔开,N表示模板树结点数,M表示第(2)中的循环操作的次数,Q 表示询问数量。
接下来N-1行,每行两个整数 fr,to,表示模板树中的一条树边。再接下来M行,每行两个整数x,to,表示将模板树中 x 为根的子树复制到大树中成为结点to的子树的一次操作。
再接下来Q行,每行两个整数fr,to,表示询问大树中结点 fr和 to之间的距离是多少。N,M,Q<=100000
Output
输出Q行,每行一个整数,第 i行是第 i个询问的答案。
Sample Input
5 2 3
1 4
1 3
4 2
4 5
4 3
3 2
6 9
1 8
5 3
Sample Output
6
3
3
HINT
经过两次操作后,大树变成了下图所示的形状:
结点6到9之间经过了6条边,所以距离为6;类似地,结点1到8之间经过了3条边;结点5到3之间也经过了3条边。
HNOI居然还有这种码农题……
突然感觉相比之下ZJOI的题质量真高……
思路:
这不是钦定了要利用模板树不变的性质嘛……
对于每一次复制,考虑只建立其中的一个节点来代表这次复制。可以认为这是类似虚树的思路。
为了方便计算,令代表节点为这次复制的子树的根节点。
于是,对于每次复制的代表节点,其父亲为它在展开树上的父亲所在的代表节点,边权为它在展开树上的父亲到其父亲的代表节点的距离+1。
然后就是求距离了。
对于询问的两个点
a
和
为了方便描述,假设
dep[rta]<dep[rtb]
。
考虑一种特殊情况:
lca(rta,rtb)==rta || lca(rta,rtb)==rtb
此时,找到
rta
在
rtb
方向上的儿子
rtson
,答案为
dis(b,rtb)+dis(rtb,rtson)+1+dis(fa[rtson],a)
对于其余情况,令
rtlca=lca(rta,rtb)
,同时令
rtsona
为
rtlca
在
rta
方向上的儿子,
rtsonb
同理。
可以得到答案为:
dis(a,rta)+dis(rta,rtsona)+1+dis(fa[rtsona],fa[rtsonb])+1+dis(rtsonb,rtb)+dis(rtb,b)
剩下的都™是细节~
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100009;
const int K=23;
int n,m,q;
inline ll read()
{
ll x=0;char ch=getchar();
while(ch<'0' || '9'<ch)ch=getchar();
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x;
}
namespace tmpt
{
int to[N<<1],nxt[N<<1],beg[N],tot;
int dep[N],fa[N][K],id[N],ed[N],rt[N],dfn;
ll dis[N];
namespace pst
{
const int M=N*20;
int t[M],ls[M],rs[M],tot;
inline int insert(int pre,int l,int r,int p)
{
int now=++tot;t[now]=t[pre]+1;
if(l==r)return now;int mid=l+r>>1;
if(p<=mid)ls[now]=insert(ls[pre],l,mid,p),rs[now]=rs[pre];
else rs[now]=insert(rs[pre],mid+1,r,p),ls[now]=ls[pre];
return now;
}
inline int query(int tl,int tr,int l,int r,int k)
{
if(l==r)return l;int mid=l+r>>1;
if(k<=t[ls[tr]]-t[ls[tl]])
return query(ls[tl],ls[tr],l,mid,k);
else
return query(rs[tl],rs[tr],mid+1,r,k-(t[ls[tr]]-t[ls[tl]]));
}
}
inline int query(int x,int k)
{
return pst::query(rt[id[x]-1],rt[ed[x]],1,n,k);
}
inline int siz(int x){return ed[x]-id[x]+1;}
inline void add(int u,int v)
{
to[++tot]=v;
nxt[tot]=beg[u];
beg[u]=tot;
}
inline void dfs(int u)
{
id[u]=++dfn;
rt[dfn]=pst::insert(rt[dfn-1],1,n,u);
if(u==1)dep[u]=1;
for(int i=beg[u];i;i=nxt[i])
if(to[i]!=fa[u][0])
{
fa[to[i]][0]=u;
for(int j=1;j<K;j++)
fa[to[i]][j]=fa[fa[to[i]][j-1]][j-1];
dep[to[i]]=dep[u]+1;
dfs(to[i]);
}
ed[u]=dfn;
}
inline int lca(int a,int b)
{
if(dep[a]>dep[b])swap(a,b);
for(int i=K-1;i>=0;i--)
if(dep[fa[b][i]]>=dep[a])
b=fa[b][i];
if(a==b)return a;
for(int i=K-1;i>=0;i--)
if(fa[a][i]!=fa[b][i])
a=fa[a][i],b=fa[b][i];
return fa[a][0];
}
inline ll dist(int a,int b)
{
return dep[a]+dep[b]-2ll*dep[lca(a,b)];
}
}
namespace bigt
{
int ori[N],stkid[N],top;
int fa[N][K],dep[N],tot;
ll stk[N],posid[N],cfa[N],dis[N],curid;
inline void buildfa(int a,int fat)
{
fa[a][0]=fat;
for(int i=1;i<K;i++)
fa[a][i]=fa[fa[a][i-1]][i-1];
}
inline int lca(int a,int b)
{
if(dep[a]>dep[b])swap(a,b);
for(int i=K-1;i>=0;i--)
if(dep[fa[b][i]]>=dep[a])
b=fa[b][i];
if(a==b)return a;
for(int i=K-1;i>=0;i--)
if(fa[a][i]!=fa[b][i])
a=fa[a][i],b=fa[b][i];
return fa[a][0];
}
inline int jump(int a,int b)
{
for(int i=K-1;~i;i--)
if(b>>i&1)
a=fa[a][i];
return a;
}
inline ll dist(int a,int b)
{
return dis[a]+dis[b]-2ll*dis[lca(a,b)];
}
inline void link(int u,int fat,ll w)
{
buildfa(u,fat);
dep[u]=dep[fat]+1;
dis[u]=dis[fat]+w;
}
inline ll findrt(ll x)
{
return stkid[upper_bound(stk+1,stk+top+1,x)-stk-1];
}
inline int orip(ll x,int rt)
{
return tmpt::query(ori[rt],x-posid[rt]+1);
}
inline void modify(int u,ll fat)
{
int now=++tot;
stk[++top]=curid+1;
posid[now]=curid+1;
stkid[top]=now;
ori[now]=u;
curid+=tmpt::siz(u);
if(fat)
{
int rt=findrt(fat);
ll w=tmpt::dist(ori[rt],orip(fat,rt));
link(now,rt,w+1ll);
cfa[now]=fat;
}
else dep[now]=1ll;
}
inline ll fadist(int x,int rt)
{
return tmpt::dep[x]-tmpt::dep[ori[rt]];
}
inline ll query(ll a,ll b)
{
int rta=findrt(a),rtb=findrt(b);
if(dep[rta]>dep[rtb])
swap(rta,rtb),swap(a,b);
int pa=orip(a,rta),pb=orip(b,rtb);
if(rta==rtb)
return tmpt::dist(pa,pb);
int rtlca=lca(rta,rtb);
if(rtlca==rta)
{
int rtson=jump(rtb,dep[rtb]-dep[rta]-1);
int fason=orip(cfa[rtson],rta);
return fadist(pb,rtb)+dist(rtb,rtson)+1+tmpt::dist(fason,pa);
}
int rtsona=jump(rta,dep[rta]-dep[rtlca]-1);
int rtsonb=jump(rtb,dep[rtb]-dep[rtlca]-1);
int fasona=orip(cfa[rtsona],rtlca);
int fasonb=orip(cfa[rtsonb],rtlca);
return dis[rta]-dis[rtsona]+1+dis[rtb]-dis[rtsonb]+1+tmpt::dist(fasona,fasonb)+fadist(pa,rta)+fadist(pb,rtb);
}
}
int main()
{
n=read();m=read();q=read();
for(int i=1,u,v;i<n;i++)
{
u=read();v=read();
tmpt::add(u,v);
tmpt::add(v,u);
}
tmpt::dfs(1);
bigt::modify(1,0);
for(int i=1;i<=m;i++)
{
ll x=read();
bigt::modify(x,read());
}
for(int i=1;i<=q;i++)
printf("%lld\n",bigt::query(read(),read()));
return 0;
}