当前位置: 首页 > news >正文

题解:AcWing 1171 距离

【题目来源】

AcWing:1171 距离 - AcWing题库

【题目描述】

给出 \(n\) 个点的一棵树,多次询问两点之间的最短距离。

注意:

  • 边是无向的。
  • 所有节点的编号是 \(1,2,\dots,n\)

【输入】

第一行为两个整数 \(n\)\(m\)\(n\) 表示点数,\(m\) 表示询问次数;

下来 \(n-1\) 行,每行三个整数 \(x,y,k\),表示点 \(x\) 和点 \(y\) 之间存在一条边长度为 \(k\)

再接下来 \(m\) 行,每行两个整数 \(x,y\),表示询问点 \(x\) 到点 \(y\) 的最短距离。

树中结点编号从 \(1\)\(n\)

【输出】

\(m\) 行,对于每次询问,输出一行询问结果。

【输入样例】

2 2
1 2 100
1 2
2 1

【输出样例】

100
100

【核心思想】

  1. 问题分析:给定一棵包含 \(n\) 个节点的带权树,边是无向的,有 \(m\) 个询问,每个询问给出节点对 \((x, y)\),需要求两点之间的最短距离。这是一个树上距离查询问题,可以利用最近公共祖先(LCA)的性质求解:\(dist(x, y) = dist(x, root) + dist(y, root) - 2 \times dist(LCA(x, y), root)\)

  2. 算法选择

    • Tarjan 离线 LCA 算法:在 DFS 过程中利用并查集维护当前搜索路径上的祖先信息,同时处理与当前节点相关的所有询问
    • DFS 预处理距离:从根节点出发进行 DFS,计算每个节点到根节点的距离 \(dist[]\)
    • 并查集维护祖先\(p[x]\) 表示节点 \(x\) 在当前搜索路径上的祖先(即 DFS 树中的父节点)
    • 离线处理询问:将所有询问保存下来,在 DFS 过程中当两个节点都访问过时即可计算答案
  3. 关键步骤

    • 建树:读入 \(n-1\) 条边 \((x, y, k)\),使用邻接表存储带权树
    • 保存询问:对于每个询问 \((a, b)\),将 \((b, id)\) 加入 \(query[a]\),将 \((a, id)\) 加入 \(query[b]\)\(id\) 为询问编号)
    • DFS 预处理距离:从根节点(节点 \(1\))开始 DFS,计算 \(dist[u]\) 表示 \(u\) 到根的距离:dist[j] = dist[u] + w
    • Tarjan LCA(DFS 过程中):
      • 标记 \(st[u] = 1\)(正在访问)
      • 递归访问所有未访问的子节点 \(j\),访问完后设置 \(p[j] = u\)(并查集合并)
      • 标记 \(st[u] = 2\)(已访问且回溯)
      • 处理询问:遍历 \(query[u]\) 中所有与 \(u\) 相关的询问 \((y, id)\)
        • \(st[y] == 2\)\(y\) 已访问且回溯),则 \(LCA(u, y) = find(y)\)
        • 计算答案:\(res[id] = dist[u] + dist[y] - 2 \times dist[LCA]\)
    • 输出结果:按询问编号输出所有答案
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n + m)\),DFS 遍历 \(O(n)\),并查集操作近似 \(O(1)\),处理每个询问 \(O(1)\)
    • 空间复杂度:\(O(n + m)\),邻接表 \(O(n)\),询问存储 \(O(m)\),并查集、距离数组、标记数组各 \(O(n)\)
    • 适用于离线查询场景(所有询问已知)
  5. Tarjan 离线 LCA 的核心思想

    • 离线处理:一次性读入所有询问,在 DFS 过程中统一处理
    • 并查集维护祖先\(p[x]\) 始终指向 \(x\) 在当前 DFS 路径上的祖先,利用并查集路径压缩快速查询
    • 访问状态标记\(st[u] = 0/1/2\) 分别表示未访问/正在访问/已访问且回溯,只有已回溯的节点才能确定 LCA
    • LCA 判定:当处理节点 \(u\) 的询问 \((u, y)\) 时,若 \(y\) 已回溯,则 \(LCA(u, y) = find(y)\),即 \(y\) 在当前 DFS 路径上的祖先
    • 树上距离公式:利用 LCA 将两点距离转化为到根距离的组合:\(dist(x, y) = dist(x) + dist(y) - 2 \times dist(LCA)\)
    • 适用场景:离线查询、需要批量处理多个 LCA 询问的问题

【解题思路】

【算法标签】

最近公共祖先

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N=100005, M=N*2, INF=1e9;
typedef pair<int, int> PII;
int n, m;  //n节点数,m询问数
struct Edge {int b, w;  //点a到点b有边,权值w
};
vector<Edge> g[N];  //图的邻接表表示,g[i]表示点i到b有边,权值wint p[N];  //p[y]:点y的根
int res[N];  //res[i] 第i个询问结果
int dist[N];  //每个点和1号点的距离
int st[N];  //节点DFS访问类型 2-访问且回溯 1-当前访问 0-没访问
//query[i][first][second]  存查询当前节点i的另一个点是first,second存查询编号
vector<PII> query[N];  //保存查询信息//求每个点和1号点(根节点)的距离dist,深搜。u, fa当前节点及其父节点
void dfs(int u, int fa)
{for (int i=0; i<g[u].size(); i++) {  //遍历u的儿子jEdge nd=g[u][i];int j=nd.b, dis=nd.w;if (j==fa) continue;  //u的邻接点是其父节点dist[j]=dist[u]+dis;dfs(j,u);  //深搜求其他节点到根的距离}
}int find(int x)  //找x的根
{if (p[x]!=x) p[x] = find(p[x]);return p[x];
}//Tarjan求LCA,u当前节点,从根节点开始深搜
void Tarjan(int u)
{st[u]=1;  //正在搜索的类型 1for (int i=0; i<g[u].size(); i++) {  //遍历u的儿子jEdge nd = g[u][i];int j=nd.b, dis=nd.w;if (st[j]==0) {  //点j没被访问,深搜访问Tarjan(j);p[j]=u;  //指定儿子j的父亲是u}}st[u]=2;  //u为根的子树访问结束,再回溯到u,标记节点u已经访问+回溯for (int i=0; i<query[u].size(); i++) {  //与当前节点相关的询问PII item = query[u][i];int y = item.first;  //当前节点u的另外一个点y,second存查询编号int id = item.second;if (st[y]==2) {  //另外一个点y已经访问且回溯int ans = find(y);  //前结点u的另外一个点y的LCAres[id] = dist[u]+dist[y]-2*dist[ans];}}
}
int main()
{cin >> n >> m;  //n节点数,m询问次数for (int i=1; i<n; i++) {  //n-1条边int a, b, c;cin >> a >> b >> c;g[a].push_back({b,c});  //建邻接表,点a到点b有边,权值1g[b].push_back({a,c});  //建邻接表}for (int i=1; i<=m; i++) {  //保存查询信息int a, b;cin >> a >> b;if (a!=b) {  //如果a=b距离就是0,不处理//query[a][first][second] 存查询当前节点a的另外一个点是b,第i个查询query[a].push_back({b,i});query[b].push_back({a,i});}}for (int i=1; i<n; i++) p[i]=i;  //初始化并查集每个父节点是它自己dfs(1, -1);  //求每个点和点1(根节点)的距离dist;<当前节点,其父节点>Tarjan(1);  //Tarjan求LCA,深搜点1(根节点)for (int i=1; i<=m; i++)cout << res[i] << endl;return 0;
}
// 使用链式前向星再写一遍
#include <bits/stdc++.h>
using namespace std;
const int N=20010, M=N*2, INF=1e9;
typedef pair<int, int> PII;
int n, m;  //n节点数,m询问数
int h[N], e[M], w[M], ne[M], idx;  //邻接表
int p[N];  //p[y]:点y的根且该根在当前搜索路径上的点
int res[N];  //res[i] 第i个询问结果
int dist[N];  //每个点和1号点的距离
int st[N];  //节点DFS访问类型 2-访问且回溯 1-当前访问 0-没访问
//query[i][first][second]  存查询当前节点i的另一个点是first,second存查询编号
vector<PII> query[N];  //保存查询信息void add (int a, int b, int c)  //建邻接表
{e[idx]=b, w[idx]=c, ne[idx]=h[a], h[a]=idx, idx++;
}//求每个点和1号点(根节点)的距离dist,深搜。u, fa当前节点及其父节点
void dfs(int u, int fa)
{for (int i=h[u]; i!=-1; i=ne[i]) {  //遍历u的儿子jint j=e[i];if (j==fa) continue;  //u的邻接点是其父节点dist[j]=dist[u]+w[i];dfs(j,u);  //深搜求其他节点到根的距离}
}int find(int x)  //找x的根
{if (p[x]!=x) p[x] = find(p[x]);return p[x];
}//Tarjan求LCA,u当前节点,从根节点开始深搜
void Tarjan(int u)
{st[u]=1;  //正在搜索的类型 1for (int i=h[u]; i!=-1; i=ne[i]) {  //遍历u的儿子jint j=e[i];if (st[j]==0) {  //点j没被访问,深搜访问Tarjan(j);p[j]=u;  //指定儿子j的父亲是u}}st[u]=2;  //u为根的子树访问结束,再回溯到u,标记节点u已经访问+回溯for (int i=0; i<query[u].size(); i++) {  //与当前节点相关的询问PII item = query[u][i];int y = item.first;  //当前节点u的另外一个点y,second存查询编号int id = item.second;if (st[y]==2) {  //另外一个点y已经访问且回溯int ans = find(y);  //前结点u的另外一个点y的LCAres[id] = dist[u]+dist[y]-2*dist[ans];}}
}
int main()
{cin >> n >> m;  //n节点数,m询问次数memset(h, -1, sizeof(h));for (int i=1; i<n; i++) {  //n-1条边int a, b, c;cin >> a >> b >> c;add(a, b, c), add(b, a, c);  //建邻接表}for (int i=1; i<=m; i++) {  //保存查询信息int a, b;cin >> a >> b;if (a!=b) {  //如果a=b距离就是0,不处理//query[a][first][second] 存查询当前节点a的另外一个点是b,第i个查询query[a].push_back({b,i});query[b].push_back({a,i});}}for (int i=1; i<n; i++) p[i]=i;  //初始化并查集每个父节点是它自己dfs(1, -1);  //求每个点和点1(根节点)的距离dist;<当前节点,其父节点>Tarjan(1);  //Tarjan求LCA,深搜点1(根节点)for (int i=1; i<=m; i++)cout << res[i] << endl;return 0;
}

【运行结果】

2 2
1 2 100
1 2
2 1
100
100
http://www.gsyq.cn/news/1534596.html

相关文章:

  • ComfyUI-Manager终极指南:3分钟学会AI绘画节点的自动化安装与管理
  • 全国优质校园课桌椅公司推荐,布局广东佛山等地区,恺力家具打造专业一站式校园家具解决方案 - 十大品牌榜
  • 2026合肥黄金出手最佳时机 旧金首饰投资金条变现技巧 - 禹竞
  • 东莞企业如何在豆包获得推荐排名?2026年GEO优化实战全攻略 - 东莞选校指南
  • 2026年昆明婚纱照行业趋势与热门风格大揭秘 - 资讯速览
  • 网络高可用实战:链路聚合与路由备份的配置排错全解析
  • 2026安徽省安庆中考200-400分的学生可以上什么学校呢?合肥理工学校根据不同分数段,开设多种升学班型! - cc江江
  • iOS Web 开发实战|iPhone 音频上传方案解析与最佳实践
  • A股日频趋势分类预测:XGBoost+滚动训练实战框架
  • 2026 年嘉兴写真照推荐哪家?业内人士实测经验来揭秘 - 资讯速览
  • Event-Driven Agent 实战:Prometheus 告警 → LLM → Tool Calling → 自动恢复
  • 2026年郴州美业技能培训机构选择指南:零基础到创业赚钱的完整路径 - 企业名录优选推荐
  • Prompt 工程炼金术:从混沌到秩序,大模型提示词优化的六重境界
  • 2026清远本地认可的 5 家排污许可废气废水监测机构实地测评汇总 废水废气 + 自行监测 + CMA 检测报告 附电话地址 - 科信检测
  • 2026揭阳本地认可的 5 家排污许可废气废水监测机构实地测评汇总 废水废气 + 自行监测 + CMA 检测报告 附电话地址 - 科信检测
  • 2026内江本地认可的 5 家排污许可废气废水监测机构实地测评汇总 废水废气 + 自行监测 + CMA 检测报告 附电话地址 - 科信检测
  • 2026莆田贵金属旧料回收优质实体店精选 5 家 黄金回收铂金白银回收真实探店测评清单 - 中业金奢再生回收中心
  • 不用大平台,外卖照样送的 4 种方法
  • 2026保姆级公章抠图完整教程!附带抠图公章制作是否违法、私刻伪造公章法律后果详解 - AI测评专家
  • 目录穿越漏洞深度解析:从路径拼接原理到Web安全实战防御
  • 题解:AcWing 1172 祖孙询问
  • 一条金项链的回收日记:选合扬上门,资质透明没踩任何坑 - 开心测评
  • 实测武汉江岸区黄金回收商圈,这些机构值得看 - 上门黄金回收
  • 全国优质功率电感服务商推荐,布局广东广州等地区,德鸿感应打造高端国产电感智造标杆 - 十大品牌榜
  • 2026长沙上门收黄金,当场称重转账,正规机构无套路 - 逸程
  • Ollama本地部署实战:从安装加速到4B模型稳定运行
  • 2026鹤壁本地认可的 5 家排污许可废气废水监测机构实地测评汇总 废水废气 + 自行监测 + CMA 检测报告 附电话地址 - 科信检测
  • KNN算法原理与工程实践:从距离度量到百万级优化
  • 2026玉树当地贵金属回收权威名录 TOP5 黄金金条铂金白银回收线下门店信息汇总 - 信誉隆金银铂奢回收
  • 2026赤峰本地防雷检测哪家专业?TOP 正规机构榜单 + 防雷装置 + 接地电阻 + SPD 检测 附电话地址 - 中安检测集团