【2018 NWERC D】Date Pickup 题解

题目大意

  有一幅 nn 个点 mm 条边的有向图,边有边权(代表通过所需时间),你在 11 号点,女朋友在 nn 号点。
  你可以选择在 11 号点延迟任意时间之后,选定一条路线开始游走,一旦开始游走就不能停下来。你的女朋友会在时间区间 [a,b][a,b] 中的任意一个实数时间点 call 你,你一旦被 call 就要马上过去 nn 号点,女朋友的等待时间就是她 call 了之后到你到达所用的时间。
  求女朋友的最坏等待时间最小。

  n,m105,  0ab1012n,m \le 10^5,\ \ 0 \le a \le b \le 10^{12},边权 106\le 10^6
  保证每个点至少有一条出边,即总是可以无限游走的。

  6s

题解

  官方题解有很多细节不是很懂,不过对我考场上的想法有很大的启发,于是完成了我考场上的想法。

  二分答案 midmid
  首先,如果到了 [a,b][a,b] 这个时间段,我们必须随叫随到,因此我们可以知道这时候哪些点哪些边是能走的:对于点 uu 需满足 dis(u,n)middis(u,n) \le mid,对于边 (u,v,w)(u,v,w) 需满足 w+dis(v,n)midw+dis(v,n) \le mid。这个时间段内我们只能在这个子图上走,除了开始的一点点(aa 时刻我们可能还在去这个子图的路上)。
  如果我们能从 11 号点去到这个子图,那么接下来要做的事情就是:如果这个子图有环,我们就一直沿着环走,就合法了;如果这个子图没有环,它就是个 DAG,就可以在上面 dp 出一个最久逗留时间,让它 b\ge b 就好了。
  所以现在就是要判断从 11 号点能不能到达这个子图,即会不会在 aa 时刻还没走到子图但是被 call 了然后去不了 nn 号点。
  假设在 [a,b][a,b] 时间段我们最先到达的节点是 xx,它的充要条件就是:1、xx 是子图上的点;2、dis(1,x)a+middis(x,n)dis(1,x) \le a+mid-dis(x,n)(即 aa 时刻 call 合法)。这样就相当于筛选出了子图的合法起点,可以做一个 bfs 筛去子图里起点不能到的点,然后做上面的拓扑找环和 dp。

  关于 DAG 上的 dp,初值是对于子图起点 xxdpx=a+middis(x,n)dp_x=a+mid-dis(x,n)(即越晚出发越好,但要满足 aa 时刻 call 合法),然后按拓扑序求最长路径。

  注意一些特殊情况,比如 11 号点本身是子图里的点的时候,只需判断 dis(1,n)middis(1,n) \le mid

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long LL;
typedef pair<int,int> pr;

const int maxn=1e5+5;

int n,m;
LL a,b;
vector<pr> e[maxn],ef[maxn];

LL dis1[maxn],disn[maxn];
bool vis[maxn];
void dijkstra(int st,vector<pr> *e,LL *dis) {
memset(dis,127,sizeof(LL)*(n+2));
dis[st]=0;
memset(vis,0,sizeof(vis));
priority_queue<pair<LL,int>> Q;
Q.push(make_pair(0,st));
fo(i,1,n) {
while (!Q.empty() && vis[Q.top().second]) Q.pop();
if (Q.empty()) break;
auto tp=Q.top(); Q.pop();
vis[tp.second]=1;
for(pr go:e[tp.second]) if (!vis[go.first] && dis[go.first]>-tp.first+go.second) {
dis[go.first]=-tp.first+go.second;
Q.push(make_pair(-dis[go.first],go.first));
}
}
}

bool valid[maxn],arrived[maxn];
vector<pr> et[maxn];
LL dp[maxn];
int dg[maxn];
void bfs(LL mid) {
memset(dg,0,sizeof(dg));
memset(arrived,0,sizeof(arrived));
queue<int> Q;
fo(i,1,n) {
et[i].clear();
if (valid[i] && dis1[i]<=a+mid-disn[i]) {
Q.push(i);
arrived[i]=1;
}
}
while (!Q.empty()) {
int cur=Q.front(); Q.pop();
for(auto go:e[cur]) if (go.second+disn[go.first]<=mid) {
if (!arrived[go.first]) Q.push(go.first), arrived[go.first]=1;
et[cur].push_back(go);
dg[go.first]++;
}
}
}
bool topo(LL mid) {
memset(dp,0xbf,sizeof(dp));
queue<int> Q;
fo(i,1,n) if (arrived[i] && !dg[i]) Q.push(i);
while (!Q.empty()) {
int cur=Q.front(); Q.pop();
if (dis1[cur]<=a+mid-disn[cur]) dp[cur]=max(dp[cur],a+mid-disn[cur]);
if (dp[cur]>=b) return 1;
for(auto go:et[cur]) {
dp[go.first]=max(dp[go.first],dp[cur]+go.second);
if (--dg[go.first]==0) Q.push(go.first);
}
}
fo(i,1,n) if (arrived[i] && dg[i]) return 1;
return 0;
}
bool check(LL mid) {
fo(i,1,n) valid[i]=(disn[i]<=mid);
if (valid[1]) return 1;
bfs(mid);
return topo(mid);
}

int main() {
scanf("%lld %lld",&a,&b);
scanf("%d %d",&n,&m);
fo(i,1,m) {
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
e[x].push_back(make_pair(y,z));
ef[y].push_back(make_pair(x,z));
}

dijkstra(1,e,dis1);
dijkstra(n,ef,disn);

LL l=0, r=dis1[n];
while (l<=r) {
LL mid=(l+r)>>1;
if (check(mid)) r=mid-1; else l=mid+1;
}

printf("%lld\n",r+1);
}