【xsy1156】 树套树(tree) 倍增
题目大意:给你m棵由n个点构成的全等的树A。这m棵树之间有m−1条边相连,组成了一棵大树。
有q组询问,每次询问这棵大树上两点之间的距离。
n,m,q≤105
这是一道小视野双倍经验题
这一题有一种显然的虚树做法,这种做法我之前打过了,这次换一种做法。
如果询问所在两点在同一棵小树内,那么显然直接倍增就可以了。
我们将连接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 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!