[JOISC 2018 Day 4] 野猪
题面
link
给定一个包含 个结点, 条带权无向连通图。
开始时有一个长度为 的序列 ,表示 JOI 君开始时在, 它要依次访问结点 但是,不能沿原路返回前一个到达的结点。序列中可能有重复结点, 但保证序列中相邻两结点不同。
接下来有 次修改, 每次修改会给出两个整数 , 表示将 修改为 。每次修改后,求满足要求的最短路径。
题解
先考虑没有不能沿原路返回怎么做?发现只需要求出任意两点之间的最短路 就行。
再考虑一下,原路返回只会在什么情况下出现?从,现在要到,但过去的最短路和过来的时候冲突了。怎么办?
那么我们对一条路径,考虑记录起点后的第一个点 ,终点前的第一个点 ,如果,这两条路径就是合法的,但是总会有不合法的情况,怎么办?
肯定还需要记录不同的走法,但是如果起点相同,终点也相同,那么这条路径肯定不会优于最短路。那么我们就记录起点终点不同的,设分别表示对应路径中起点后的第一个点和终点前的第一个点,我们记录这四条路径:
- 最短路
- 所有中的最短路
- 所有中的最短路
- 所有中的最短路
为什么就是这 条路径?因为这4条边包含了所有可能碰到的冲突情况,比如若最短路的 冲突,那么有第 条路径,而若 的 冲突则有第 条路径,而且这些路径都是在最优不满足的情况下的次优选择。
之后线段树就可以维护修改了,pushup
的时候可以暴力枚举左边和右边的路径两两组合,之后依次枚举选出这四条路径。
最后就只剩如何对每对点,求出路径的这四条路径。发现这个时候我们用点表示最短路(即记录从 到点 的最短路)无法求出其他三条路径,怎么办?
用点不行,那么我们就用边表示,因为边刚好包含了点的转移,我们枚举第一步走的哪一条边,就确定了起点和,最后也可以知道终点和。
但是这样我们的最短路就要改一下,如果直接像TAX那题一样,边化点,点化边,再优化连边会很麻烦。这里我们可以简单一点。考虑直接跑dij,只是最后用边记录答案,那么有哪些边最后没有记录答案?其实就是顺着最短路路径的反向边,这也是用点表示最短路不能展现的贡献方案。
那么我们可以记录到达点 的边 ,那么我们再一次遍历到点 的时候就可以顺着 返回前面的点,从而更新所有的边。代码:
ll d[maxn << 1];
int v[maxn];
priority_queue<pair<ll, int>> p;
void zuidl(int S) {
memset(v, 0, sizeof(v));
memset(d, 0x3f, sizeof(d));
d[S] = a[S].z;
p.push(mp(-d[S], S));
while (p.size()) {
int id = p.top().se;
ll D = -p.top().fi;
p.pop();
int x = a[id].y;
if (D > d[id] || v[x] < 0)continue;
if (!v[x]) {
v[x] = id;//记录到达x的点的编号
for (int i = h[x]; i; i = a[i].ne) {
if (i == (id ^ 1) )continue;//不能通过上一个边的反边
if (d[i] > d[id] + a[i].z) {
d[i] = d[id] + a[i].z;
p.push(mp(-d[i], i));
}
}
} else {
int y = v[x] ^ 1;// 反向边
v[x] = -1;
if (d[y] > d[id] + a[y].z) {
d[y] = d[id] + a[y].z;
p.push(mp(-d[y], y));
}
}
}
}
启发
- 记录路径这里背吧...可以用来处理满足条件下的最值问题。
- 需要得到一条路径起点的下一个点,终点的前一个点,而且还需要多条路径,就按上面的最短路方法。
本文作者:qwq_123
本文链接:https://www.cnblogs.com/qwq-123/p/15905800.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步