【xsy1156】 树套树(tree) 倍增

 

题目大意:给你$m$棵由$n$个点构成的全等的树$A$。这$m$棵树之间有$m-1$条边相连,组成了一棵大树。

有$q$组询问,每次询问这棵大树上两点之间的距离。

$n,m,q≤10^5$

 

这是一道小视野双倍经验题

这一题有一种显然的虚树做法,这种做法我之前打过了,这次换一种做法。

如果询问所在两点在同一棵小树内,那么显然直接倍增就可以了。

 

 

我们将连接$m$棵树的边抽出来,构成另外一棵树(暂且称作树$B$),节点$p$表示第$p$棵树,我们钦定这棵树的$1$号点为根。

我们令$up1[i]$表示树$B$中,连接$i$号点$father$所在的边,它在第$i$棵小树中所连接的节点。

令$up2[i]$表示树$B$中,连接$i$号点$father$所在的边,它在第$fa[i]$棵小树中所连接的点。

令$dis[i]$表示从树$B$的第$1$号树的$1$号节点开始,到达小树$i$第$up1[i]$号节点的距离。

 

对于一组询问$(w,x,y,z)$,我们求出$w,y$的$lca$,令$w'$表示在$w$到$lca$的路径上,离$lca$最近的点(不包括$lca$本身),同理求出$y'$。

设最终答案为$ans$

若存在$w'$,则$ans+=A.dis(x,up1[x])+dis[w]-dis[w']$,并令$x=up2[w']$。

若存在$y'$,则$ans+=A.dis(z,up1[z])+dis[y]-dis[y']$,并令$z=up2[y']$。

最后$ans+=A.dis(x,z)$即可。

注意细节

 1 #include<bits/stdc++.h>
 2 #define M 100005
 3 #define L long long
 4 using namespace std;
 5 int n,m,q;
 6 struct tree1{
 7     struct edge{int u,next;}e[M*2]; int head[M],use=0;
 8     void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;}
 9     int f[M][20],dep[M];
10     void dfs(int x,int fa){
11         f[x][0]=fa; dep[x]=dep[fa]+1;
12         for(int i=1;i<20;i++) f[x][i]=f[f[x][i-1]][i-1];
13         for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa) dfs(e[i].u,x);
14     }
15     int getlca(int x,int y){
16         if(dep[x]<dep[y]) swap(x,y); int cha=dep[x]-dep[y];
17         for(int i=19;~i;i--) if((1<<i)&cha) x=f[x][i];
18         for(int i=19;~i;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
19         if(x==y) return x; return f[x][0];
20     }
21     int getdis(int x,int y){
22         int lca=getlca(x,y);
23         return dep[x]+dep[y]-2*dep[lca];
24     }
25     void init(){
26         for(int i=1;i<n;i++){
27             int x,y; scanf("%d%d",&x,&y);
28             add(x,y); add(y,x);
29         }
30         dfs(1,0);
31     }
32 }a;
33 struct tree2{
34     struct edge{int u,us,ut,next;}e[M*2]; int head[M],use=0;
35     void add(int x,int y,int us,int ut){use++;e[use].u=y;e[use].us=us;e[use].ut=ut;e[use].next=head[x];head[x]=use;}
36     int f[M][20],dep[M],up1[M],up2[M]; L dis[M];
37     void dfs(int x,int fa){
38         f[x][0]=fa; dep[x]=dep[fa]+1;
39         for(int i=1;i<20;i++) f[x][i]=f[f[x][i-1]][i-1];
40         for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
41             up1[e[i].u]=e[i].ut; up2[e[i].u]=e[i].us;
42             dis[e[i].u]=dis[x]+a.getdis(up1[x],e[i].us)+1;
43             dfs(e[i].u,x);
44         }
45     }
46     int getlca(int x,int y){
47         if(dep[x]<dep[y]) swap(x,y); int cha=dep[x]-dep[y];
48         for(int i=19;~i;i--) if((1<<i)&cha) x=f[x][i];
49         for(int i=19;~i;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
50         if(x==y) return x; return f[x][0];
51     }
52     int jump(int x,int k){
53         for(int i=19;~i;i--) if((1<<i)&k) x=f[x][i];
54         return x;
55     }
56     void init(){
57         for(int i=1;i<m;i++){
58             int x,X,y,Y; scanf("%d%d%d%d",&x,&X,&y,&Y);
59             add(x,y,X,Y); add(y,x,Y,X);
60         }
61         dfs(1,0);
62     }
63 }b;
64 int main(){
65     scanf("%d%d%d",&n,&m,&q);
66     a.init();
67     b.init();
68     while(q--){
69         int x,X,y,Y; scanf("%d%d%d%d",&x,&X,&y,&Y);
70         int lca=b.getlca(x,y); 
71         L ans=0;
72         if(x!=lca){
73             ans+=a.getdis(X,b.up1[x]);
74             int xx=b.jump(x,b.dep[x]-b.dep[lca]-1);
75             ans+=b.dis[x]-b.dis[xx]+1;
76             X=b.up2[xx];
77         }
78         if(y!=lca){
79             ans+=a.getdis(Y,b.up1[y]);
80             int yy=b.jump(y,b.dep[y]-b.dep[lca]-1);
81             ans+=b.dis[y]-b.dis[yy]+1;
82             Y=b.up2[yy];
83         }
84         ans+=a.getdis(X,Y);
85         printf("%lld\n",ans);
86     }
87 }

 

转载于:https://www.cnblogs.com/xiefengze1/p/9811817.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值