1 条题解
-
0
自动搬运
来自洛谷,原作者为

高天昊
这个家伙很懒,但也好歹留下了点什么搬运于
2025-08-24 21:58:29,当前版本为作者最后更新于2018-10-10 13:13:13,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
关于这道题倍增求LCA的做法
看到大佬们都用树链剖分做,可我太菜了不会树链剖分......
不过这道题如果用RMQ做的话只有80分
好像是因为我不会卡常所以步入正题
关于这道题为什么求LCA其他题解已经解释得很明确了,所以我只是概述一下。
题目要求得出三个点走到同一个点所要求的最小花费,经过我们的分析可以得出这个公共的点较其他点优的情况可以通过 求三个点两两之间的LCA得出,然后我们发现这三个LCA中有二者重合即它存在两种情况:最后三者所走到的最优公共点只可能为这二者之一。
然后经过分析可以得到不重合的公共点才是最优解,大家可以拿笔模拟一下或者感性理解(不重合的公共点情况下 一个单独的点移动比另两个点移动距离要多,较另外一种情况花费当然更低)。
既然是几个人走到一起最小花费,显然是求LCA的同时维护一下深度,~~当然这是倍增所必需的
然后运用差分的思想(假设两点 一点深度为d1,另一点深度为d2,它们LCA深度为d3,这二者之间的距离即为d1+d1-2*d3,只要将这两点推广成三点即可)计算一下最小花费(这个第一篇题解已经非常详细,不会的同学可以去看一看)
#include<cstdio> #include<iostream> #include<cmath> using namespace std; long long ans=0; int fa[500010][25],lg[500010],deep[500010],t; struct node { int from; int to; int next; }ed[2*500001]; int v[2*500001],tot=0; void add(int x,int y) { ed[++tot].from=x; ed[tot].to=y; ed[tot].next=v[x]; v[x]=tot; } int lca(int x,int y) { if(deep[x]<deep[y]) //假设x深度大于y swap(x,y); while(deep[x]>deep[y]) //x,y调整到同一深度 { x=fa[x][lg[deep[x]-deep[y]]-1]; } if(x==y) return x; for(int k=lg[deep[x]];k>=0;k--) //x,y一起向上跳 { if(fa[x][k]!=fa[y][k]) { x=fa[x][k]; y=fa[y][k]; } } return fa[x][0]; } void dfs(int x,int fath) { deep[x]=deep[fath]+1; //处理深度 fa[x][0]=fath; for(int i=1;(1<<i)<=deep[x];i++) { fa[x][i]=fa[fa[x][i-1]][i-1]; } for(int i=v[x];i;i=ed[i].next) if(ed[i].to!=fath) dfs(ed[i].to,x); } int n,m; int a,b,c; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n-1;i++) { scanf("%d%d",&a,&b); add(a,b); add(b,a); } dfs(1,0); for(int i=1;i<=n;i++) //常数优化 lg[i]=lg[i-1]+(1<<lg[i-1]==i); for(int i=1;i<=m;i++) { ans=0; scanf("%d%d%d",&a,&b,&c); int t1=lca(a,b); //三者分别求LCA int t2=lca(a,c); int t3=lca(b,c); if(t1==t2) t=t3; else if(t1==t3) t=t2; else if(t2==t3) t=t1; //差分 ans=deep[a]+deep[b]+deep[c]-deep[t1]-deep[t2]-deep[t3]; printf("%d %lld\n",t,ans); } return 0; }这份题解可能会有瑕疵,敬请各位大佬斧正。
- 1
信息
- ID
- 3249
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者