[题解]P9432 [NAPC-#1] rStage5 - Hard Conveyors
P9432 [NAPC-#1] rStage5 - Hard Conveyors
题意简述
给定一个\(N\)个节点的树形结构,其中有\(k\)个关键节点。
接下来有\(q\)次询问,每次询问给定\(x,y\),请输出\(x\)到\(y\)至少经过一个关键点的最短路径。
解题思路
我们发现,这道题相当于让我们从\(x\)到\(y\)的简单路径上,额外扩展出一路程去到达一个关键点。那么从哪里开始,到达哪一个关键点能消耗最少的步数呢?
我们不难想到,可以定义\(mindis[i]\)表示\(i\)到最近关键点的距离。这样答案就是:
\]
先考虑\(mindis\)怎样计算。我们可以巧妙地用Dijkstra来求(只需要在堆优化Dijkstra的基础上稍作修改即可:初始化时,把所有关键点距离设为\(0\),并将它们入队列)。这一步骤时间复杂度\(O(n\log n)\)。
但我们还可以用\(O(n)\)的方法求出。\(mindis\)初始化为\(+\infty\),过程分为\(2\)次DFS。
- 第\(1\)次DFS:
- 如果\(u\)是关键点,那么\(mindis[u]=0\)。
- 给子节点\(i\)赋初值\(mindis[i]=mindis[u]+w(u,i)\)(\(w\)表示边权),并搜索子节点。
- 回溯时用子节点\(i\)的值更新\(u\):\(mindis[u]=\min(mindis[u],mindis[i]+w(u,i))\)。
- 第\(2\)次DFS:
- 第\(1\)次DFS可能会导致一些节点的\(mindis\)没有被更新,此时我们需要用它们父节点最终的\(mindis\)来更新子节点:即\(mindis[i]=\min(mindis[i],mindis[u]+w(u,i))\)。
- 搜索子节点,重复上述步骤。
\(mindis\)求出来了,但如果我们用\(O(n)\)的复杂度去遍历\(x\sim y\)路径上的所有节点,就会超时。
我们可以用倍增的思想来优化这一过程。用\(minn[i]\)来表示每个点往上\(2^i\)层的\(mindis\)。因为待会求\(dist(x,y)\)需要用LCA,所以\(minn\)就在预处理LCA的时候一块计算出来。
求\(dist(x,y)\)的话,用\(dis[u]\)表示\(u\)到根节点的距离(预处理LCA时计算出来),答案就是\(dis[x]+dis[y]-dis[lca(x,y)]\)。
怎么求\(\min\limits_{i是x\sim y路径上的节点}(mindis[i])\)呢?我们仍然用倍增的思想,让\(x,y\)往上跳,每跳一次用\(minn\)更新一下最小的\(mindis\),和求LCA的方法十分相似,所以就和求\(dist(x,y)\)合并成一个函数\(lca(x,y)\)了。返回值是一个pair。first是LCA,second是最小的\(mindis\)。
用上面的式子得出结果即可,别忘了\(\times 2\)。
两种\(mindis\)求法的时间复杂度都是\(O((n+q)\log N)\),但显然DFS求法更高效。
实现细节:
- 如果用\(O(n)\)的方法求\(mindis\),别忘了距离数组初始化为\(+\infty\),但别太大,因为有\(+1\)操作。
Code
DFS求$mindis$
#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define N 100010
using namespace std;
int n,q,k,dis[N],mindis[N];
//dis表示到根的距离
//mindis表示到关键点的最小距离
//minn维护每个点往上2^x层的mindis
int dep[N],fa[N][20],minn[N][20];
bool is[N],vis[N];
struct edge{int to,w;};
vector<edge> G[N];
void dfs1(int u,int father){
if(is[u]) mindis[u]=0;
for(auto i:G[u]){
if(i.to==father) continue;
mindis[i.to]=mindis[u]+i.w;
dfs1(i.to,u);
mindis[u]=min(mindis[u],mindis[i.to]+i.w);
}
}
void dfs2(int u,int father){
for(auto i:G[u]){
if(i.to==father) continue;
mindis[i.to]=min(mindis[i.to],mindis[u]+i.w);
dfs2(i.to,u);
}
}
void dfs(int u,int father){
dep[u]=dep[father]+1;
fa[u][0]=father;
for(int i=1;i<20;i++)
fa[u][i]=fa[fa[u][i-1]][i-1],
minn[u][i]=min(minn[u][i-1],minn[fa[u][i-1]][i-1]);
for(auto i:G[u])
if(i.to!=father)
minn[i.to][0]=min(mindis[i.to],mindis[u]),
dis[i.to]=dis[u]+i.w,
dfs(i.to,u);
}
pair<int,int> lca(int u,int v){
//first:LCA second:minn
if(u==v) return {u,mindis[u]};
int ans=INT_MAX;
if(dep[u]<dep[v]) swap(u,v);
for(int i=19;i>=0;i--)
if(dep[fa[u][i]]>=dep[v]){
ans=min(ans,minn[u][i]),u=fa[u][i];
}
if(u==v) return {u,ans};
for(int i=19;i>=0;i--)
if(fa[u][i]!=fa[v][i])
ans=min(ans,min(minn[u][i],minn[v][i])),
u=fa[u][i],v=fa[v][i];
ans=min(ans,min(minn[u][0],minn[v][0]));
return {fa[u][0],ans};
}
int solve(int u,int v){
auto t=lca(u,v);
return dis[u]+dis[v]-2*dis[t.first]+2*t.second;
}
signed main(){
memset(mindis,0x7f,sizeof mindis);
cin>>n>>q>>k;
for(int i=1;i<n;i++){
int u,v,w;
cin>>u>>v>>w;
G[u].push_back({v,w});
G[v].push_back({u,w});
}
for(int i=1;i<=k;i++){
int u;
cin>>u;
is[u]=1;
}
dfs1(1,0);
dfs2(1,0);
dfs(1,0);
while(q--){
int x,y;
cin>>x>>y;
cout<<solve(x,y)<<"\n";
}
return 0;
}
Dijkstra求$mindis$
#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define N 100010
using namespace std;
int n,q,k,dis[N],mindis[N];
//dis表示到根的距离
//mindis表示到关键点的最小距离
//minn维护每个点往上2^x层的mindis
int dep[N],fa[N][20],minn[N][20];
bool is[N],vis[N];
struct edge{int to,w;};
vector<edge> G[N];
void dijkstra(){
priority_queue<PII,vector<PII>,greater<PII>> heap;
while(!heap.empty()) heap.pop();
for(int i=1;i<=n;i++){
if(!is[i]) mindis[i]=INT_MAX;
else heap.push({0,i});
}
while(!heap.empty()){
auto u=heap.top().second;
heap.pop();
if(vis[u]) continue;
vis[u]=1;
for(auto i:G[u]){
if(mindis[u]+i.w<mindis[i.to]){
mindis[i.to]=mindis[u]+i.w;
heap.push({mindis[i.to],i.to});
}
}
}
}
void dfs(int u,int father){
dep[u]=dep[father]+1;
fa[u][0]=father;
for(int i=1;i<20;i++)
fa[u][i]=fa[fa[u][i-1]][i-1],
minn[u][i]=min(minn[u][i-1],minn[fa[u][i-1]][i-1]);
for(auto i:G[u])
if(i.to!=father)
minn[i.to][0]=min(mindis[i.to],mindis[u]),
dis[i.to]=dis[u]+i.w,
dfs(i.to,u);
}
pair<int,int> lca(int u,int v){
//first:LCA second:minn
if(u==v) return {u,mindis[u]};
int ans=INT_MAX;
if(dep[u]<dep[v]) swap(u,v);
for(int i=19;i>=0;i--)
if(dep[fa[u][i]]>=dep[v]){
ans=min(ans,minn[u][i]),u=fa[u][i];
}
if(u==v) return {u,ans};
for(int i=19;i>=0;i--)
if(fa[u][i]!=fa[v][i])
ans=min(ans,min(minn[u][i],minn[v][i])),
u=fa[u][i],v=fa[v][i];
ans=min(ans,min(minn[u][0],minn[v][0]));
return {fa[u][0],ans};
}
int solve(int u,int v){
auto t=lca(u,v);
return dis[u]+dis[v]-2*dis[t.first]+2*t.second;
}
signed main(){
cin>>n>>q>>k;
for(int i=1;i<n;i++){
int u,v,w;
cin>>u>>v>>w;
G[u].push_back({v,w});
G[v].push_back({u,w});
}
for(int i=1;i<=k;i++){
int u;
cin>>u;
is[u]=1;
}
dijkstra();
dfs(1,0);
while(q--){
int x,y;
cin>>x>>y;
cout<<solve(x,y)<<"\n";
}
return 0;
}
[题解]P9432 [NAPC-#1] rStage5 - Hard Conveyors的更多相关文章
- 2016 华南师大ACM校赛 SCNUCPC 非官方题解
我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...
- noip2016十连测题解
以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...
- BZOJ-2561-最小生成树 题解(最小割)
2561: 最小生成树(题解) Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1628 Solved: 786 传送门:http://www.lyd ...
- Codeforces Round #353 (Div. 2) ABCDE 题解 python
Problems # Name A Infinite Sequence standard input/output 1 s, 256 MB x3509 B Restoring P ...
- 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解
题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...
- 2016ACM青岛区域赛题解
A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Jav ...
- poj1399 hoj1037 Direct Visibility 题解 (宽搜)
http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...
- 网络流n题 题解
学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...
- CF100965C题解..
求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...
- JSOI2016R3 瞎BB题解
题意请看absi大爷的blog http://absi2011.is-programmer.com/posts/200920.html http://absi2011.is-programmer.co ...
随机推荐
- LUNARiA
本文同步发布于我的网站 也算是头一次在没有任何安利和剧透,仅在看了简介的情况下就直接下单并开始游玩一部gal了.果然,没有给我留下什么遗憾呢. 游玩日志 SKYOUT-FOREVER <LUNA ...
- 理解 .NET 结构体字段的内存布局
目录 前言 基本概念 结构体的默认字段布局 对齐 64 位系统与 32 位系统的对齐要求差异 默认字段布局中 对齐要求 与 偏移量 的关系 填充 包含引用类型字段的结构体的默认字段布局 用 Struc ...
- 你应该懂的AI大模型(六)之 transformers
一.Transformer与transformers 结论:Transformer是模型架构,transfortmers是库. 问:为什么我们要知道Transformer与transformers呢? ...
- Tauri2.0-DeepSeek电脑端Ai对话|tauri2+vite6+deepseek流式ai聊天系统
重磅新作tauri2.0+vue3.5+deepseek+arco桌面客户端ai流式输出聊天对话系统. tauri2-vue3-deepseek:桌面端ai聊天对话,基于Tauri2.x+Vite6集 ...
- Golang基础笔记二之字符串及其操作
本文首发于公众号:Hunter后端 原文链接:Golang基础笔记二之字符串及其操作 这一篇笔记主要介绍 Golang 字符串相关处理,以下是本篇笔记目录: 字符串的定义和初始化 字符 字符串操作 1 ...
- Eplan是什么软件?学习Eplan软件的几个关键要点
EPLAN是一款电气计算机辅助设计软件.我是一名Eplan软件的学习者,最近在学习这个专业的电气设计软件时,总结了一些关键要点,希望能与大家分享. 1. 熟悉软件界面和功能:首先,我们需要熟悉Epla ...
- 一次说清楚:CAE软件可以做什么?
引言: 计算机辅助工程(CAE)软件是现代工程设计与分析的重要工具,它以计算机技术为基础,利用数值模拟和仿真方法来解决各种工程问题.本文将探讨CAE软件的广泛应用领域,以及它在工程设计与分析中的作用. ...
- fowsniff WP
下载地址: https://download.vulnhub.com/fowsniff/Fowsniff_CTF_ova.7z category:重要 awk剪切得到字典,巩固awk使用技巧 motd ...
- 一文带你全面解析postman工具的使用(基础篇) ---九五小庞
postman是一款支持http协议的接口调试与测试工具,其主要特点就是功能强大,使用简单且易用性好 .无论是开发人员进行接口调试,还是测试人员做接口测试,postman都是我们的首选工具之一 .那么 ...
- CPU 负载高,到底应不应该告警?
CPU 负载高,到底应不应该告警? 不告警吧,出了问题怕被怼,嫌你告警缺失 告警吧,好像全是噪音,工程师都自动忽略了 尴尬... 成年人的世界没有非黑即白,如果要严肃的论述,就要加很多限定词,为了避免 ...