[洛谷P5304][GXOI/GZOI2019]旅行者
题目大意: 有一张 \(n(n\leqslant10^5)\) 个点 \(m(m\leqslant5\times10^5)\) 条边的有向有正权图,有\(k(2\leqslant k\leqslant n)\)个关键点。问图中最近的两个关键点的距离。多组数据。
题解: 有两种方法。
可对\(k\)个关键点二进制分组,可以知道\(x\)与\(y\)不同至少满足二进制上有一位不同,每次按二进制中的一位分成两组,,建一个虚点连向所有的源点,跑 \(dijkstra\) ,总共跑 \(\log_2n\) 次。复杂度 \(O(n\log_2^2n)\) 。代码我没写
令 \(F_i\) 表示可以走到第\(i\)个点的最近关键点,\(f_i\) 为 \(dis_{F_i,i}\) , \(T_i\) 表示第\(i\)个点可以走到的最近关键点,\(t_i\) 为 \(dis_{i.T_i}\) 。由题解得,枚举一条边 \((u,v,w)\) ,若 \(F_u\not=T_v\) ,那么用 \(f_u+w+t_v\) 更新答案。这些数组可以在正反图上跑以所有关键点为源点的最短路径得到。接下来证明这样为什么是对的。
很显然可以证明,若最短路径为 \(x\to y\) ,其中边 \((u,v,w)\) 在这条路径上且 \(F_u\not=T_v\) ,那么 \(F_u=x,T_v=y\) (假定最短路径只有一条),则 \(f_u+w+t_v=ans\) 。也就是只要证明\(x\to y\)的路径上至少有一条边满足\(F_u\not=T_v\)
采用反证的方法。比如一张图如下,其中\(1\to n\)为最短路径,方点为关键点,设边权均为\(w\)
graph LR;
A[1]
B((2))
C((3))
D((n-1))
E[n]
A--A-->B
B--B-->C
C-.-D
D--Z-->E对于边\(A\)来说,\(F_u=1,T_v=n\),要破坏,因为\(F_u\)无法改变,只能改变\(T_v\),可以连一条\(2\to 1\)的边,边权小于\(w\),那么边\(A\)就无法对答案产生贡献。再考虑边\(B\),\(F_u=1,T_v=n\),要破坏,有两种方法,一种是连一条\(n\to 2\)的边权小于\(w\)的边,另一种是连一条\(3\to 1\)的边权小于\(w\)的边。由于第一种方案会产生一条\(n\to 2\to 1\)的更短的路径,所以排去,只能用第二种。以此类推,最后会连一条\(n\to 1\)的边导致出现了更短的路径。与原来的假设矛盾。故原命题成立
卡点: 无
C++ Code:
#include <iostream>
#include <algorithm>
#include <cstring>
#define maxn 100010
#define maxm 500010
int cnt, head[maxn], _head[maxn];
struct Edge {
int to, nxt, w;
} e[maxm], _e[maxm];
inline void addedge(int a, int b, int c) {
e[++cnt] = (Edge) {b, head[a], c}; head[a] = cnt;
_e[cnt] = (Edge) {a, _head[b], c}; _head[b] = cnt;
}
int Tim, n, m, k;
int To[maxn], _To[maxn];
long long dis[maxn], _dis[maxn];
bool mar[maxn];
namespace Graph {
int V[maxn << 2];
long long *Dis;
inline int getmin(int a, int b) { return Dis[a] < Dis[b] ? a : b; }
void modify(int rt, int l, int r, int p, int num) {
if (l == r) {
V[rt] = num;
return ;
}
const int mid = l + r >> 1;
if (p <= mid) modify(rt << 1, l, mid, p, num);
else modify(rt << 1 | 1, mid + 1, r, p, num);
V[rt] = getmin(V[rt << 1], V[rt << 1 | 1]);
}
void dijkstra(int *head, Edge *e, int *To, long long *dis) {
const int SZ = maxn << 3;
memset(dis, 0x3f, SZ);
Dis = dis;
memset(V, 0, sizeof V);
for (int i = 1; i <= n; ++i) To[i] = i;
for (int i = 1; i <= n; ++i) if (mar[i]) {
dis[i] = 0;
modify(1, 1, n, i, i);
}
for (int Tim = n; Tim; --Tim) {
int u = V[1];
modify(1, 1, n, u, 0);
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (dis[v] > dis[u] + e[i].w) {
dis[v] = dis[u] + e[i].w;
To[v] = To[u];
modify(1, 1, n, v, v);
}
}
}
}
}
using Graph::dijkstra;
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
std::cin >> Tim;
while (Tim --> 0) {
std::cin >> n >> m >> k;
for (int i = 0, a, b, c; i < m; ++i) {
std::cin >> a >> b >> c;
addedge(a, b, c);
}
memset(mar, false, sizeof mar);
for (int i = 0, x; i < k; ++i) std::cin >> x, mar[x] = true;
dijkstra(_head, _e, To, dis);
dijkstra(head, e, _To, _dis);
long long ans = 0x3f3f3f3f3f3f3f3f;
for (int i = 1, u, v; i <= m; ++i) {
u = _e[i].to, v = e[i].to;
if (_To[u] != To[v]) ans = std::min(ans, _dis[u] + e[i].w + dis[v]);
}
std::cout << ans << '\n';
if (Tim) {
memset(head, 0, sizeof head);
memset(_head, 0, sizeof _head);
cnt = 0;
}
}
return 0;
}
[洛谷P5304][GXOI/GZOI2019]旅行者的更多相关文章
- 洛谷 P5304 [GXOI/GZOI2019]旅行者(最短路)
洛谷:传送门 bzoj:传送门 参考资料: [1]:https://xht37.blog.luogu.org/p5304-gxoigzoi2019-lv-xing-zhe [2]:http://www ...
- P5304 [GXOI/GZOI2019]旅行者
题目地址:P5304 [GXOI/GZOI2019]旅行者 这里是官方题解 一个图 \(n\) 点 \(m\) 条边,里面有 \(k\) 个特殊点,问这 \(k\) 个点之间两两最短路的最小值是多少? ...
- 洛谷.5300.[GXOI/GZOI2019]与或和(单调栈)
LOJ BZOJ 洛谷 想了一个奇葩的单调栈,算的时候要在中间取\(\min\),感觉不靠谱不写了=-= 调了十分钟发现输出没取模=v= BZOJ好逗逼啊 题面连pdf都不挂了 哈哈哈哈 枚举每一位. ...
- luogu P5304 [GXOI/GZOI2019]旅行者
传送门 所以这个\(5s\)是SMG 暴力是枚举每一个点跑最短路,然后有一个很拿衣服幼稚的想法,就是把所有给出的关键点当出发点,都丢到队列里,求最短路的时候如果当前点\(x\)某个相邻的点\(y\)是 ...
- P5304 [GXOI/GZOI2019]旅行者(最短路/乱搞)
luogu bzoj Orz自己想出神仙正解的sxy 描述略 直接把所有起点推进去跑dijkstra... 并且染色,就是记录到这个点的最短路是由哪个起点引导出来的 然后再把所有边反指跑一次... 之 ...
- 【题解】Luogu P5304 [GXOI/GZOI2019]旅行者
原题传送门 题意:给你k个点,让你求两两最短路之间的最小值 我们考虑二进制拆分,使得每两个点都有机会分在不同的组\((A:0,B:1)\)中,从源点\(S\)向\(A/B\)中的点连边权为0的边,从\ ...
- 【BZOJ5506】[GXOI/GZOI2019]旅行者(最短路)
[BZOJ5506][GXOI/GZOI2019]旅行者(最短路) 题面 BZOJ 洛谷 题解 正着做一遍\(dij\)求出最短路径以及从谁转移过来的,反过来做一遍,如果两个点不由同一个点转移过来就更 ...
- [LOJ3087][GXOI/GZOI2019]旅行者——堆优化dijkstra
题目链接: [GXOI/GZOI2019]旅行者 我们考虑每条边的贡献,对每个点求出能到达它的最近的感兴趣的城市(设为$f[i]$,最短距离设为$a[i]$)和它能到达的离它最近的感兴趣的城市(设为$ ...
- 洛谷 P 5 3 0 4 [GXOI/GZOI2019]旅行者
题目描述 J 国有 n 座城市,这些城市之间通过 m 条单向道路相连,已知每条道路的长度. 一次,居住在 J 国的 Rainbow 邀请 Vani 来作客.不过,作为一名资深的旅行者,Vani 只对 ...
随机推荐
- html转为图片插件:html2canvas保存图片模糊问题解决
使用官网的CDN: <script src="http://html2canvas.hertzen.com/dist/html2canvas.min.js"></ ...
- CDH 6.0.1 版本 默认配置下 HUE | happybase 无法访问 Hbase 的问题
第一个问题 HUE 无法直接连接到 HBase 在默认配置下 CDH 6.0.1 版本下的 HBase2.0 使用了默认配置 hbase.regionserver.thrift.compact = T ...
- php在Linux下的相对路径问题
如图所示,我在 /root/phpcode/ 下面有两个php文件. a.php 与 b.php,我用 a.php 去 require b.php ,然后 b.php 输出 1. 现在我在 /root ...
- 【LA 3942】 Remember the word
题意 给定一个字符串和若干个单词,询问能把字符串分解成这些单词的方案数.比如abcd ,有单词a,b,ab,cd:就可以分解成a+b+cd或者ab+cd. 分析 trie树—>DP 代码 (感谢 ...
- RUN vs CMD vs ENTRYPOINT
参考:https://www.ibm.com/developerworks/community/blogs/132cfa78-44b0-4376-85d0-d3096cd30d3f/entry/RUN ...
- 忘记zip密码咋办?python在手密码我有
整理资料时发现几个 zip 文件的密码忘记了,于是尝试用python暴力破解 首先是读取和解压zip文件,使用 zipfile 库 import zipfile z = zipfile.ZipFile ...
- how does SELECT TOP works when no order by is specified?
how does SELECT TOP works when no order by is specified? There is no guarantee which two rows you ge ...
- 并发下sftp连接报错——com.jcraft.jsch.JSchException: connection is closed by foreign host
当对单接口极限测试时,随着并发量上升,接口稳定性出现不稳定的情况,排查后台日志,发现报错在该接口调用sftp上传时出现问题(确切的是在初始化连接时失败) 原因:系统SSH终端连接数配置过小,查看虚拟机 ...
- 部分NLP工程师面试题总结
面试题 https://www.cnblogs.com/CheeseZH/p/11927577.html 其他 大数据相关面试题 https://www.cnblogs.com/CheeseZH/p/ ...
- hive中function函数查询
1. desc function [函数名] desc function xpath; 查询用法: 2. desc function extended [函数名] desc function exte ...