题目大意: 有一张 \(n(n\leqslant10^5)\) 个点 \(m(m\leqslant5\times10^5)\) 条边的有向有正权图,有\(k(2\leqslant k\leqslant n)\)个关键点。问图中最近的两个关键点的距离。多组数据。

题解: 有两种方法。

  1. 可对\(k\)个关键点二进制分组,可以知道\(x\)与\(y\)不同至少满足二进制上有一位不同,每次按二进制中的一位分成两组,,建一个虚点连向所有的源点,跑 \(dijkstra\) ,总共跑 \(\log_2n\) 次。复杂度 \(O(n\log_2^2n)\) 。代码我没写

  2. 令 \(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]旅行者的更多相关文章

  1. 洛谷 P5304 [GXOI/GZOI2019]旅行者(最短路)

    洛谷:传送门 bzoj:传送门 参考资料: [1]:https://xht37.blog.luogu.org/p5304-gxoigzoi2019-lv-xing-zhe [2]:http://www ...

  2. P5304 [GXOI/GZOI2019]旅行者

    题目地址:P5304 [GXOI/GZOI2019]旅行者 这里是官方题解 一个图 \(n\) 点 \(m\) 条边,里面有 \(k\) 个特殊点,问这 \(k\) 个点之间两两最短路的最小值是多少? ...

  3. 洛谷.5300.[GXOI/GZOI2019]与或和(单调栈)

    LOJ BZOJ 洛谷 想了一个奇葩的单调栈,算的时候要在中间取\(\min\),感觉不靠谱不写了=-= 调了十分钟发现输出没取模=v= BZOJ好逗逼啊 题面连pdf都不挂了 哈哈哈哈 枚举每一位. ...

  4. luogu P5304 [GXOI/GZOI2019]旅行者

    传送门 所以这个\(5s\)是SMG 暴力是枚举每一个点跑最短路,然后有一个很拿衣服幼稚的想法,就是把所有给出的关键点当出发点,都丢到队列里,求最短路的时候如果当前点\(x\)某个相邻的点\(y\)是 ...

  5. P5304 [GXOI/GZOI2019]旅行者(最短路/乱搞)

    luogu bzoj Orz自己想出神仙正解的sxy 描述略 直接把所有起点推进去跑dijkstra... 并且染色,就是记录到这个点的最短路是由哪个起点引导出来的 然后再把所有边反指跑一次... 之 ...

  6. 【题解】Luogu P5304 [GXOI/GZOI2019]旅行者

    原题传送门 题意:给你k个点,让你求两两最短路之间的最小值 我们考虑二进制拆分,使得每两个点都有机会分在不同的组\((A:0,B:1)\)中,从源点\(S\)向\(A/B\)中的点连边权为0的边,从\ ...

  7. 【BZOJ5506】[GXOI/GZOI2019]旅行者(最短路)

    [BZOJ5506][GXOI/GZOI2019]旅行者(最短路) 题面 BZOJ 洛谷 题解 正着做一遍\(dij\)求出最短路径以及从谁转移过来的,反过来做一遍,如果两个点不由同一个点转移过来就更 ...

  8. [LOJ3087][GXOI/GZOI2019]旅行者——堆优化dijkstra

    题目链接: [GXOI/GZOI2019]旅行者 我们考虑每条边的贡献,对每个点求出能到达它的最近的感兴趣的城市(设为$f[i]$,最短距离设为$a[i]$)和它能到达的离它最近的感兴趣的城市(设为$ ...

  9. 洛谷 P 5 3 0 4 [GXOI/GZOI2019]旅行者

    题目描述 J 国有 n 座城市,这些城市之间通过 m 条单向道路相连,已知每条道路的长度. 一次,居住在 J 国的 Rainbow 邀请 Vani 来作客.不过,作为一名资深的旅行者,Vani 只对 ...

随机推荐

  1. 2017.10.1 国庆清北 D1T1 zhx的字符串题

    题目背景 2017国庆清北D1T1 题目描述 你是能看到第一题的 friends 呢. ——hja 何大爷对字符串十分有研究,于是天天出字符串题虐杀 zhx.何大爷今天为 字符串定义了新的权值计算方法 ...

  2. 2、zookeeper原理

    一.Zookeeper的角色 » 领导者(leader),负责进行投票的发起和决议,更新系统状态 » 学习者(learner),包括跟随者(follower)和观察者(observer),follow ...

  3. 「SNOI2019」积木

    传送门 Description 有一块\(n\)行\(m\)列的网格板, \(n,m\)都是奇数.网格上平铺着一些\(1*2\)的积木.积木可以旋转,不能重叠.网格板上只有一格的空位. 你可以做两种操 ...

  4. linux桌面系统开启windows远程访问

    本文基于centos系统,且默认你的桌面系统已经安装完成 1.在linux中安装如下软件 sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epe ...

  5. 再谈CAP

    CAP定理设计者Eric Brewer作为Google基础设施副总裁在时隔二十年后重谈CAP定律. Eric Brewer目前正在推动Kubernetes和容器建设,在这篇采访中:Google sys ...

  6. Spark2.x(六十):在Structured Streaming流处理中是如何查找kafka的DataSourceProvider?

    本章节根据源代码分析Spark Structured Streaming(Spark2.4)在进行DataSourceProvider查找的流程,首先,我们看下读取流数据源kafka的代码: Spar ...

  7. 性能测试指标:TPS,吞吐量,并发数,响应时间

    性能测试指标:TPS,吞吐量,并发数,响应时间 常用的网站性能测试指标有:TPS.吞吐量.并发数.响应时间.性能计数器等. 并发数并发数是指系统同时能处理的请求数量,这个也是反应了系统的负载能力. 响 ...

  8. Linux配置crontab

    1. 添加任务(每分钟执行一次)crontab -e* * * * * /home/lings/logRotate.sh 2. 查看日志Jun 5 20:25:01 localhost CROND[8 ...

  9. 决策树分析、EMV(期望货币值)

    名称:决策树分析.EMV(期望货币值) 定义:它利用了概率论的原理,并且利用一种树形图作为分析工具.其基本原理是用决策点代表决策问题,用方案分枝代表可供选择的方案,用概率分枝代表方案可能出现的各种结果 ...

  10. 【IoT】物联网NB-IoT之电信物联网开放平台对接流程浅析

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/liwei16611/article/de ...