题目大意:
现在有一棵模板树,有一棵大树。大树一开始为模板树。
现在给出
m
m
m个操作,每次给出
a
,
b
a,b
a,b,然后把
a
a
a和它的子树接到
b
b
b上。
有
q
q
q次询问,每次给出
u
,
v
u,v
u,v,要求输出大树上
u
,
v
u,v
u,v的距离。
n
,
m
,
q
<
=
1
0
5
n,m,q<=10^5
n,m,q<=105
具体细节见题面QWQ。
实现方法
如果直接用暴力的方法建树,不难发现,每次最多新增
n
n
n个点,总共
m
m
m次操作,复杂度是
O
(
n
m
)
O(nm)
O(nm)……
因此要换一种方法建图。
思考把模板树的子树接到大树上的过程,发现后面树的结构和模板树的一部分是一样的。
所以对于每个接上去的子树,记录
l
[
i
]
,
r
[
i
]
l[i],r[i]
l[i],r[i],表示这棵子树编号的左右区间,即
l
[
i
]
≤
l[i] \leq
l[i]≤子树内编号
≤
r
[
i
]
\leq r[i]
≤r[i]。
把大树看作一棵树套树,以样例为例:
为了方便,这里把普通节点叫做“小节点”,接上去的子树和原来的模板树叫做“大节点”。
因为图中小节点很小,大节点很大qwq
那么图中共有9个小节点,3个大节点。
小节点不用记录信息(记录信息会爆空间),对于每个大节点,记录:
(如果不想看变量(函数)解释,珂以往下拉)
r
o
o
t
[
i
]
root[i]
root[i],表示第
i
i
i个大节点对应在模板树中的根。
也就是说,把以
u
u
u为根的子树作为第
i
i
i个大节点,那么
r
o
o
t
[
i
]
=
u
root[i]=u
root[i]=u。
图中
r
o
o
t
[
1
]
=
1
,
r
o
o
t
[
2
]
=
4
,
r
o
o
t
[
3
]
=
3.
root[1]=1,root[2]=4,root[3]=3.
root[1]=1,root[2]=4,root[3]=3.
f
a
t
h
e
r
[
i
]
father[i]
father[i],表示第
i
i
i个大节点在树上的小节点父亲。
图中
f
a
t
h
e
r
[
1
]
=
0
,
f
a
t
h
e
r
[
2
]
=
3
,
f
a
t
h
e
r
[
3
]
=
2.
father[1]=0,father[2]=3,father[3]=2.
father[1]=0,father[2]=3,father[3]=2. (图中箭头指向的就是
f
a
t
h
e
r
father
father)
b
i
g
a
n
c
[
i
]
[
j
]
biganc[i][j]
biganc[i][j],表示在大树中,向上跳
2
j
2^j
2j步所到达的大节点。用来求大树上的LCA。
图中
b
i
g
a
n
c
[
2
]
[
0
]
=
b
i
g
a
n
c
[
3
]
[
0
]
=
1
biganc[2][0]=biganc[3][0]=1
biganc[2][0]=biganc[3][0]=1
b
i
g
d
i
s
t
[
i
]
[
j
]
bigdist[i][j]
bigdist[i][j],表示在大树中,向上跳
2
j
2^j
2j步,到达
b
i
g
a
n
c
[
i
]
[
j
]
biganc[i][j]
biganc[i][j]的根所跳过的距离。用来计算答案。
图中
b
i
g
d
i
s
t
[
2
]
[
0
]
=
2
,
b
i
g
d
i
s
t
[
3
]
[
0
]
=
3.
bigdist[2][0]=2,bigdist[3][0]=3.
bigdist[2][0]=2,bigdist[3][0]=3.
b
i
g
d
e
e
p
[
i
]
bigdeep[i]
bigdeep[i],表示在大树中,大节点
i
i
i的深度。
图中
b
i
g
d
e
e
p
[
1
]
=
0
,
b
i
g
d
e
e
p
[
2
]
=
b
i
g
d
e
e
p
[
3
]
=
1.
bigdeep[1]=0,bigdeep[2]=bigdeep[3]=1.
bigdeep[1]=0,bigdeep[2]=bigdeep[3]=1.
为了求出结果,还要定义几个函数辅助计算:
G
e
t
R
o
o
t
(
u
)
GetRoot(u)
GetRoot(u),表示查询编号为
u
u
u的小节点在哪一个大节点中。
图中
G
e
t
R
o
o
t
(
2
)
=
1
,
G
e
t
R
o
o
t
(
6
)
=
2
,
G
e
t
R
o
o
t
(
9
)
=
3.
GetRoot(2)=1,GetRoot(6)=2,GetRoot(9)=3.
GetRoot(2)=1,GetRoot(6)=2,GetRoot(9)=3.
G
e
t
P
o
s
(
u
)
GetPos(u)
GetPos(u),表示查询编号为
u
u
u的小节点在模板树中的位置。
图中
G
e
t
P
o
s
(
3
)
=
3
,
G
e
t
P
o
s
(
8
)
=
5
,
G
e
t
P
o
s
(
9
)
=
3.
GetPos(3)=3,GetPos(8)=5,GetPos(9)=3.
GetPos(3)=3,GetPos(8)=5,GetPos(9)=3.
假设现在有一个强劲的
a
l
b
alb
alb程序帮你把这些都算好了,那么想怎么求树上点对之间的距离:
仍然是样例的图,以求8和9之间的距离为例。
首先应该把8和9跳到各自大节点的根。
9已经是根节点,不用管。8会跳到7,此时
a
n
s
=
1
ans=1
ans=1。
然后按照倍增求LCA的方法向上让两个大节点跳到同一高度。这里已经在同一高度,不用管。
模拟倍增求LCA的过程,让它们跳到LCA的两个孩子出,这里已经满足条件,不用管。
令
r
t
u
rtu
rtu为跳
u
u
u过程中大节点的指针,
r
t
v
rtv
rtv同理。
那么现在
r
t
u
=
3
,
r
t
v
=
2
rtu=3,rtv=2
rtu=3,rtv=2。
让两端分别向上跳到它们的小节点父亲出,即
r
t
u
=
2.
r
t
v
=
3.
rtu=2.rtv=3.
rtu=2.rtv=3. 此时
a
n
s
=
3.
ans=3.
ans=3.(又跳了两次)
然后问题就转化为
a
n
s
+
d
i
s
t
(
2
,
3
)
ans+dist(2,3)
ans+dist(2,3)。
在模板树上乱搞一下即可。
但是,这样会有一种情况不满足:
考虑求5,9之间的距离。
如果按上面的方法,把5跳到1,9跳到2,那么问题转化为
a
n
s
+
d
i
s
t
(
1
,
2
)
ans+dist(1,2)
ans+dist(1,2)。最后得到的答案是5。
但是如果观察一下5,9之间真正的距离,会发现它们之间距离仅为3。
不难发现,边(1,4)是不必要走的,但在上面的走法中多走了2次。
又发现,这种反例当且仅当一个大节点是另一个大节点的祖先时出现。
于是考虑换一下策略:
仍然令
r
t
u
rtu
rtu表示
u
u
u跳到的大节点的指针,
r
t
v
rtv
rtv表示
v
v
v跳到的大节点的指针。
不妨设
b
i
g
d
e
e
p
[
r
t
u
]
>
=
b
i
g
d
e
e
p
[
r
t
v
]
bigdeep[rtu]>=bigdeep[rtv]
bigdeep[rtu]>=bigdeep[rtv](如果不是,swap一下即可)。
如果
b
i
g
d
e
e
p
[
r
t
u
]
>
b
i
g
d
e
e
p
[
r
t
v
]
bigdeep[rtu]>bigdeep[rtv]
bigdeep[rtu]>bigdeep[rtv],那么把
r
t
u
rtu
rtu跳到深度为
b
i
g
d
e
e
p
[
r
t
v
]
+
1
bigdeep[rtv]+1
bigdeep[rtv]+1的位置,
r
t
v
rtv
rtv不跳,把答案统计入
a
n
s
ans
ans。
判断:如果
r
t
u
rtu
rtu的大节点父亲为
r
t
v
rtv
rtv,那么说明
r
t
v
rtv
rtv是
r
t
u
rtu
rtu的祖先。
那么把
r
t
u
rtu
rtu跳到小节点父亲
f
a
t
h
e
r
[
r
t
u
]
father[rtu]
father[rtu]处,然后求一下它与
v
v
v之间的距离,加到
a
n
s
ans
ans里面即可。
如果
r
t
v
rtv
rtv不为
r
t
u
rtu
rtu的祖先,那么再让
r
t
u
rtu
rtu跳到它的大节点父亲,记录一下答案,然后按照之前的方法倍增计算即可。
实现细节
1.
1.
1.大树内节点的距离要转化为模板树内节点的距离(显然)。
2.
2.
2.找小节点在模板树里的编号时,暴力做肯定是不可取的。
这里考虑找出它所在的大节点的根,然后发现大节点内编号顺序是确定的。
那么转化成一个区间第
k
k
k小问题,用主席树乱搞即可。
3.
3.
3.查询第
u
u
u个点在哪个大节点内,由于知道了大节点内的子节点编号范围,所以二分查找一下就珂以了qwq。
4.
4.
4.注意
l
o
n
g
long
long
l
o
n
g
long
long
!
!
!
毒瘤代码
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#include<vector>
#define re register int
#define rl register ll
using namespace std;
typedef long long ll;
ll read() {
rl x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
namespace I_Love {
const int Size=100005;
const int LOG=19;
int n,m,q,cnt,head[Size];
ll deep[Size];
struct Edge {
int v,next;
ll t;
} w[Size<<1];
void AddEdge(int u,int v) {
w[++cnt].v=v;
w[cnt].next=head[u];
head[u]=cnt;
}
int tim,dfn[Size],out[Size],order[Size],anc[Size][LOG];
ll siz[Size];
void dfs(int x,int fa) {
deep[x]=deep[fa]+1;
order[++tim]=x;
dfn[x]=tim;
anc[x][0]=fa;
for(re i=1; i<LOG; i++) {
anc[x][i]=anc[anc[x][i-1]][i-1];
}
siz[x]=1;
for(int i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
if(nxt!=fa) {
dfs(nxt,x);
siz[x]+=siz[nxt];
}
}
out[x]=tim;
}
int LCA(int u,int v) {
if(deep[u]<deep[v]) swap(u,v);
for(re i=18; i>=0; i--) {
if(deep[anc[u][i]]>=deep[v]) {
u=anc[u][i];
}
}
if(u==v) return u;
for(re i=18; i>=0; i--) {
if(anc[u][i]!=anc[v][i]) {
u=anc[u][i];
v=anc[v][i];
}
}
return anc[u][0];
}
//主席树查询区间第k小,找小节点在模板树里的编号
int tot,T[Size];
int ls[Size*LOG],rs[Size*LOG],sum[Size*LOG];
int update(int pre,int l,int r,int v) {
int rt=++tot;
ls[rt]=ls[pre]; rs[rt]=rs[pre];
sum[rt]=sum[pre]+1;
if(l<r) {
int mid=(l+r)>>1;
if(v<=mid) {
ls[rt]=update(ls[pre],l,mid,v);
} else {
rs[rt]=update(rs[pre],mid+1,r,v);
}
}
return rt;
}
int query(int u,int v,int l,int r,int k) {
if(l==r) return l;
int mid=(l+r)>>1,x=sum[ls[v]]-sum[ls[u]];
if(k<=x) return query(ls[u],ls[v],l,mid,k);
return query(rs[u],rs[v],mid+1,r,k-x);
}
//大树的信息
ll father[Size],root[Size],biganc[Size][LOG],bigdist[Size][LOG];
int bigcnt,bighead[Size];
ll num,l[Size],r[Size];
//num表示当前大节点个数,[l[i],r[i]]表示第i个大节点对应的区间
Edge bigw[Size<<1];
int GetRoot(ll u) {
//查询第u个小节点在哪一个大节点中
int pos=upper_bound(l+1,l+1+num,u)-l-1;
if(l[pos]<=u && r[pos]>=u) {
return pos;
} else if(l[pos+1]<=u && r[pos+1]>=u) {
return pos+1;
} else {
return pos-1;
}
}
int GetPos(ll u) {
//查询第u个小节点在模板树中的位置
int bigrt=GetRoot(u);
int rt=root[bigrt];
//rt的子树范围:[dfn[rt],out[rt]]
//u是第u-l[bigrt]+1小
return query(T[dfn[rt]-1],T[out[rt]],1,n,u-l[bigrt]+1);
}
void AddBigEdge(ll u,ll v,ll fa) {
//u,v表示大树中的大节点,fa表示v在大树中的小节点父亲
//加一条大树边
bigw[++bigcnt].v=v;
//边权为fa的deep-root的deep
bigw[bigcnt].t=deep[GetPos(fa)]-deep[root[u]]+1;
bigw[bigcnt].next=bighead[u];
bighead[u]=bigcnt;
father[v]=fa;
}
ll bigdeep[Size];
inline ll Dist(int u,int v) {
return deep[u]+deep[v]-(deep[LCA(u,v)]<<1);
}
inline ll GetBigFather(ll u) {
//返回u的大节点的根在模板树中的编号
return root[GetRoot(u)];
}
ll GetDist(ll u,ll v) {
ll rtu=GetRoot(u);
ll rtv=GetRoot(v);
if(rtu==rtv) {
return Dist(GetPos(u),GetPos(v));
}
if(bigdeep[rtu]<bigdeep[rtv]) {
swap(u,v);
swap(rtu,rtv);
}
ll ans=deep[GetPos(u)]-deep[root[rtu]];
//先让rtu跳到比rtv深度多1的位置
ll tmpu=rtu,tmpv=rtv;
for(re i=18; i>=0; i--) {
if(bigdeep[biganc[rtu][i]]>bigdeep[rtv]) {
ans+=bigdist[rtu][i];
rtu=biganc[rtu][i];
}
}
if(biganc[rtu][0]==rtv) {
//v是u的祖先
ll fa=father[rtu]; //取rtu在大树中的小节点父亲
return ans+Dist(GetPos(fa),GetPos(v))+1;
}
//v不是u的祖先
if(bigdeep[rtu]>bigdeep[rtv]) {
ans+=bigdist[rtu][0];
rtu=biganc[rtu][0];
}
ans+=deep[GetPos(v)]-deep[root[rtv]];
for(re i=18; i>=0; i--) {
if(biganc[rtu][i]!=biganc[rtv][i]) {
ans+=bigdist[rtu][i]+bigdist[rtv][i];
rtu=biganc[rtu][i];
rtv=biganc[rtv][i];
}
}
ans+=2; //跳到lca
rtu=father[rtu];
rtv=father[rtv];
return ans+Dist(GetPos(rtu),GetPos(rtv));
}
void Fujibayashi_Ryou() {
// freopen("tree13.in","r",stdin);
// freopen("WA.txt","w",stdout);
n=read();
m=read();
q=read();
for(re i=1; i<n; i++) {
int u=read();
int v=read();
AddEdge(u,v);
AddEdge(v,u);
}
dfs(1,0);
root[1]=1;
for(re i=1; i<=n; i++) {
T[i]=update(T[i-1],1,n,order[i]);
}
l[num=1]=1,r[1]=n;
for(re i=1; i<=m; i++) {
//把模板树的u复制到v
ll u=read();
ll v=read();
num++;
l[num]=r[num-1]+1;
r[num]=l[num]+siz[u]-1;
root[num]=u;
int rt=GetRoot(v);
father[num]=v;
bigdeep[num]=bigdeep[rt]+1;
biganc[num][0]=rt;
bigdist[num][0]=deep[GetPos(v)]-deep[root[rt]]+1;
for(re j=1; j<=16; j++) {
biganc[num][j]=biganc[biganc[num][j-1]][j-1];
bigdist[num][j]=bigdist[num][j-1]+bigdist[biganc[num][j-1]][j-1];
}
}
while(q--) {
ll u=read();
ll v=read();
printf("%lld\n",GetDist(u,v));
}
}
}
int main() {
I_Love::Fujibayashi_Ryou();
return 0;
}