Codeforces Round #372 (Div. 2) D. Complete The Graph

链接

https://codeforces.com/contest/716/problem/D

题意

无向图中,给权值可变边赋值,使最短路为 $L$。

思路一

忽略可变边,若最短路 $<L$,无解。

将可变边全部赋值为 $1$,若最短路 $>L$,无解。

否则,有解。

设当前最短路径为 $A$,将 $A$ 上的一条可变边增大到使 $A$ 上边权和为 $L$,其余不在 $A$ 上的可变边赋值为 $>=L$ 的值。

这次操作中修改过的边都打上标记,那么还剩下 $A$ 中其余为 $1$ 的可变边。

再次跑最短路,如果最短路 $\neq L$,将最短路径上的一条未标记的可变边增大到使最短路为 $L$,并给这条边打上标记。

总共最多会跑 $n$ 次最短路。

时间复杂度:$O(nm\log(m))$

代码一

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
102
#include <bits/stdc++.h>
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(),(x).end()
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
using namespace std;
typedef double DB;
typedef long double LD;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;
// head
const LL INF=0x3f3f3f3f3f3f3f3f;
const int N=1e3+5;
const int M=1e4+5;
int n,m,s,t,a[M],b[M],pre[N],id[N][N],path[N];
LL L,c[M],dist[N];
bool f[M],ff[M],st[N];
VPII g[N];
VI e;
struct R {
LL w;
int u;
R() {}
R(LL w,int u):w(w),u(u) {}
bool operator < (const R &T) const {
return w>T.w;
}
};
LL dijkstra() {
for(int i=1;i<=n;i++) {
dist[i]=INF;
st[i]=false;
}
priority_queue<R> q;
q.emplace(0,s);
dist[s]=0;
while(SZ(q)) {
int u=q.top().u;
q.pop();
if(st[u]) continue;
st[u]=true;
for(auto x:g[u]) {
int v=x.FI;
LL w=c[x.SE];
if(!st[v]&&dist[u]+w<dist[v]) {
dist[v]=dist[u]+w;
pre[v]=u;
if(f[x.SE]) path[v]=x.SE;
else path[v]=path[u];
q.emplace(dist[v],v);
}
}
}
return dist[t];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>m>>L>>s>>t;
++s,++t;
for(int i=1;i<=m;i++) {
cin>>a[i]>>b[i]>>c[i];
++a[i],++b[i];
id[a[i]][b[i]]=id[b[i]][a[i]]=i;
if(c[i]) {
g[a[i]].EB(b[i],i);
g[b[i]].EB(a[i],i);
}
else f[i]=true,e.PB(i);
}
if(dijkstra()<L) return cout<<"NO"<<'\n',0;
for(auto x:e) {
c[x]=1;
g[a[x]].EB(b[x],x);
g[b[x]].EB(a[x],x);
}
if(dijkstra()>L) return cout<<"NO"<<'\n',0;
int tt=0;
for(int u=pre[t],v=t;u;v=u,u=pre[u]) if(f[id[u][v]]) {
ff[id[u][v]]=true;
c[id[u][v]]=1;
tt=id[u][v];
}
c[tt]=L-dist[t]+c[tt];
for(auto x:e) if(!ff[x]) {
f[x]=false;
c[x]=1e10;
}
while(dijkstra()!=L) {
f[path[t]]=false;
c[path[t]]=L-dist[t]+c[path[t]];
}
cout<<"YES"<<'\n';
for(int i=1;i<=m;i++) cout<<a[i]-1<<' '<<b[i]-1<<' '<<c[i]<<'\n';
return 0;
}

思路二

设可变边集合为 $e$,每条边初始值为 $0$,考虑依次给每条可变边 $+1$

即:$e_1+1,e_2+1,e_3+1,\dots,e_k+1,e_1+1,e_2+1,\dots$

每次最短路最多 $+1$

二分 $+1$ 次数跑 dijkstra

时间复杂度:$O(\log(mL)m\log(m))$

代码二

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
#include <bits/stdc++.h>
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(),(x).end()
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
using namespace std;
typedef double DB;
typedef long double LD;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;
// head
const LL INF=0x3f3f3f3f3f3f3f3f;
const int N=1e3+5;
const int M=1e4+5;
int n,m,s,t,a[M],b[M];
LL L,c[M],dist[N];
bool st[N];
VPII g[N];
VI e;
struct R {
LL w;
int u;
R() {}
R(LL w,int u):w(w),u(u) {}
bool operator < (const R &T) const {
return w>T.w;
}
};
LL dijkstra() {
for(int i=1;i<=n;i++) {
dist[i]=INF;
st[i]=false;
}
priority_queue<R> q;
q.emplace(0,s);
dist[s]=0;
while(SZ(q)) {
int u=q.top().u;
q.pop();
if(st[u]) continue;
st[u]=true;
for(auto x:g[u]) {
int v=x.FI;
LL w=c[x.SE];
if(!st[v]&&dist[u]+w<dist[v]) {
dist[v]=dist[u]+w;
q.emplace(dist[v],v);
}
}
}
return dist[t];
}
LL check(LL mid) {
for(auto x:e) c[x]=mid/m+(x<=mid%m);
return dijkstra();
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>m>>L>>s>>t;
++s,++t;
for(int i=1;i<=m;i++) {
cin>>a[i]>>b[i]>>c[i];
++a[i],++b[i];
g[a[i]].EB(b[i],i);
g[b[i]].EB(a[i],i);
if(!c[i]) e.PB(i);
}
LL l=m,r=L*m+1;
while(l<=r) {
LL mid=l+r>>1;
LL ret=check(mid);
if(ret==L) {
cout<<"YES"<<'\n';
for(int i=1;i<=m;i++) cout<<a[i]-1<<' '<<b[i]-1<<' '<<c[i]<<'\n';
return 0;
}
else if(ret>L) r=mid-1;
else l=mid+1;
}
cout<<"NO"<<'\n';
return 0;
}

思路三

将可变边全部赋值为 $1$,若最短路 $>L$,无解。

记第一次跑最短路时从起点到 $i$ 的最短路为 $dist_{i,0}$,$diff=L-dist_{t,0}$。

再次跑最短路,记为 $dist_{i,1}$,对于每一条可变边 $x\rightarrow y$,如果 $dist_{x,1}+w<dist_{y,0}+diff$,那么将该边边权修改为 $dist_{y,0}+diff-dist_{x,1}$。

最后判断 $dist_{t,1}$ 是否 $=L$。

解释:

第一次跑完最短路后,我们求出了需要给最短路增加 $diff$ 才能满足题目要求,

如果我们随意找一条最短路径上的可变边增加 $diff$,那么当前路径虽然值为 $L$,但不一定最短。

所以我们让存在可变边的路径都增加 $diff$,那么最终最短路一定为 $L$,除非存在一条比 $L$ 小且没有可变边的路径。

时间复杂度:$O(m\log(m))$

代码三

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
#include <bits/stdc++.h>
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(),(x).end()
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
using namespace std;
typedef double DB;
typedef long double LD;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;
// head
const LL INF=0x3f3f3f3f3f3f3f3f;
const int N=1e3+5;
const int M=1e4+5;
int n,m,s,t,a[M],b[M];
LL L,c[M],dist[N][2];
bool st[N],f[M];
VPII g[N];
struct R {
LL w;
int u;
R() {}
R(LL w,int u):w(w),u(u) {}
bool operator < (const R &T) const {
return w>T.w;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>m>>L>>s>>t;
++s,++t;
for(int i=1;i<=m;i++) {
cin>>a[i]>>b[i]>>c[i];
++a[i],++b[i];
g[a[i]].EB(b[i],i);
g[b[i]].EB(a[i],i);
if(!c[i]) c[i]=1,f[i]=true;
}
for(int i=1;i<=n;i++) dist[i][0]=dist[i][1]=INF;
priority_queue<R> q;
q.emplace(0,s);
dist[s][0]=dist[s][1]=0;
while(SZ(q)) {
int u=q.top().u;
q.pop();
if(st[u]) continue;
st[u]=true;
for(auto x:g[u]) {
int v=x.FI;
LL w=c[x.SE];
if(!st[v]&&dist[u][0]+w<dist[v][0]) {
dist[v][0]=dist[u][0]+w;
q.emplace(dist[v][0],v);
}
}
}
LL diff=L-dist[t][0];
if(diff<0) return cout<<"NO"<<'\n',0;
for(int i=1;i<=n;i++) st[i]=false;
q.emplace(0,s);
while(SZ(q)) {
int u=q.top().u;
q.pop();
if(st[u]) continue;
st[u]=true;
for(auto x:g[u]) {
int v=x.FI;
LL &w=c[x.SE];
if(f[x.SE]) {
if(dist[u][1]+w<dist[v][0]+diff) w=dist[v][0]+diff-dist[u][1];
}
if(!st[v]&&dist[u][1]+w<dist[v][1]) {
dist[v][1]=dist[u][1]+w;
q.emplace(dist[v][1],v);
}
}
}
if(dist[t][1]==L) {
cout<<"YES"<<'\n';
for(int i=1;i<=m;i++) cout<<a[i]-1<<' '<<b[i]-1<<' '<<c[i]<<'\n';
}
else cout<<"NO"<<'\n';
return 0;
}