【xsy1156】 树套树(tree) 倍增

 

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

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

n,m,q105

 

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

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

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

 

 

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

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

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

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

 

对于一组询问(w,x,y,z),我们求出w,ylca,令w表示在wlca的路径上,离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 }
复制代码

 

posted @   AlphaInf  阅读(246)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示