题意
给一个n个点m条边的有向图,每条边有边权的取值范围。给出一条点1到点2的路径,问在这条路径上先按顺序走多少条边后,再走到点2(长度要包括之前规划好的路径)一定没有直接从1号点开始优。
n
,
m
≤
2
×
1
0
5
n,m\leq2\times10^5
n,m≤2×105
边权1e6
思路
GDKOI2018原题LK真牛批- 考虑每条边的取值,可以感受到不是最大值就是最小值。若存在一种图G使得某种要求的走法是最短路,那么将这条路取最小值,其余取最大值,不会对最短路造成影响。
- 注意到答案是可以二分的,现在就是要判断按照规定的路径走到x后,是否在某种图中存在x到2的最短路。
- 类似上文的想法,搞一个贪心出来。规定路径上x之前的边都取最小值当然优。
- 接下来的操作就很高端了:
- 直觉地想象一下,最好是构造使得从1出发的路径取最大,从x出发的路径取最小。但是会有路径相交的问题。
- 若路径要相交,则会有重点。有重点的情况是“无需讨论”的。因为走到重点的顺序就已经决定了他们接下来最终到2的顺序。因此某个点往后扩展时,必定只会由1或x中较小的(相同则是x)接手。
- 自然地我们整出这样一个贪心想法:
- 从1和x开始,初始dis[1]=0.dis[x]= ∑ L \sum L ∑L,每次选出最小的一个点,若是被1占领的,则往外扩展取最大值。否则取最小值。但如果是规划好的前缀,则直接取最小值。最终终点若是x占领的,那么是存在这样一条最短路的。
- 本质上就是一个dij操作。。。
- 为了实现方便,将边权*2之后将dis[x]初始减一。这样方便优先x以及判断是谁占领。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int n, m, p, tot;
int final[N], nex[N], to[N], L[N], R[N], no[N];
struct edge{
int from, to, L, R;
} e[N];
int ed[N], pre[N];
ll dis[N];
bool ok(int mid) {
static priority_queue<pair<ll,int> > pq;
memset(dis, 127, sizeof dis);
int ori = e[ed[mid]].to;
dis[1] = dis[ori] = 0;
for(int i = 1; i <= mid; i++) {
dis[ori] += e[ed[i]].L;
}
dis[ori]--;
pq.push(make_pair(-dis[1], 1));
pq.push(make_pair(-dis[ori], ori));
while(pq.size()){
pair<ll,int> z = pq.top(); pq.pop();
int x = z.second;
if (dis[x] != -z.first) continue;
for(int i = final[x]; i; i = nex[i]) {
int y = to[i];
ll ev = (dis[x] & 1 ? e[no[i]].L : e[no[i]].R);
if (1 <= pre[i] && pre[i] <= mid) ev = e[no[i]].L;
ll v = dis[x] + ev;
if (v < dis[y]) {
dis[y] = v;
pq.push(make_pair(-dis[y], y));
}
}
}
return dis[2] & 1;
}
int main() {
freopen("travel.in","r",stdin);
// freopen("travel.out","w",stdout);
cin >> n >> m >> p;
for(int i = 1; i <= m; i++) {
int u = 0, v = 0;
scanf("%d %d %d %d", &u, &v, &e[i].L, &e[i].R);
e[i].from = u, e[i].to = v;
e[i].L *= 2;
e[i].R *= 2;
to[++tot] = v, nex[tot] = final[u], final[u] = tot;
no[tot] = i;
}
for(int i = 1; i <= p; i++) {
scanf("%d", &ed[i]);
pre[ed[i]] = i;
}
int l = 1, r = p, ans = 0;
while (l <= r) {
if (ok(l + r>> 1)) {
ans = l = l + r >> 1;
l++;
} else r = (l + r >> 1) - 1;
}
if (ans == p) printf("No Response!\n");
else printf("%d\n", ed[ans + 1]);
}