T1 - Farm Tour

题目传送门

题目大意

现在有 $N$ 个节点,有 $M$ 条边,要从 $1$ 走到 $N$ 然后再回到 $1$。要求走的边不能重复,求最短路径。

解题思路

很简单,我们直接把这个图作为网络的一部分,然后另外开一个源点和汇点,发送 2 的流量,走出来的一个最小费用最大流就是我们想要的答案。

正确代码

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <map>
#include <set>
#include <functional>
#include <queue>
#include <cmath>
#include <cstdlib>
using namespace std;

typedef long long ll;
// typedef __int128 int128;
typedef pair<int , int > pii;
typedef unsigned long long ull;

namespace FastIO
{
// char buf[1 << 20], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++)
template<typename T> inline T read(T& x) {
x = 0;
int f = 1;
char ch;
while (!isdigit(ch = getchar())) if (ch == '-') f = -1;
while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
x *= f;
return x;
}
template<typename T, typename... Args> inline void read(T& x, Args &...x_) {
read(x);
read(x_...);
return;
}
inline ll read() {
ll x;
read(x);
return x;
}
};
using namespace FastIO;

template<int N, int M>
class Graph {
private :
struct Edge {
int fr, to, nt;
ll wt, fl, cp;
Edge() {}
Edge(int fr, int to, int nt, ll wt, ll cp, ll fl) :
fr(fr), to(to), nt(nt), wt(wt), cp(cp), fl(fl) {}
}e[M];
int hd[N], cnte;
public :
inline void clear() { memset(hd, 0, sizeof(hd)), cnte = 0; }
inline void AddEdge(int u, int v, ll w = 0, ll c = 0, ll f = 0) {
e[++cnte] = Edge(u, v, hd[u], w, c, f);
hd[u] = cnte;
}
inline int head(int u) { return hd[u]; }
inline int fr(int u) { return e[u].fr; }
inline int nt(int u) { return e[u].nt; }
inline int to(int u) { return e[u].to; }
inline ll wt(int u) { return e[u].wt; }
inline ll& flw(int u) { return e[u].fl; }
inline ll& cap(int u) { return e[u].cp; }
};

const int MaxV = 2010;
const int MaxE = 40010;
const ll INF = 1e18;

int n, m;
Graph< MaxV, MaxE >G;

inline void Input() {
read(n, m);
int u, v, c;
for(int i = 1; i <= m; i++) {
read(u, v, c);
G.AddEdge(u, v, c, 1);
G.AddEdge(v, u,-c, 0);
G.AddEdge(v, u, c, 1);
G.AddEdge(u, v,-c, 0);
}
}

ll dis[MaxV];
int path[MaxV], vis[MaxV];

inline bool SPFA(int S, int T, int nodenum) {
queue<int >q;
memset(vis, 0, sizeof(vis));
memset(path,-1,sizeof(path));
for(int i = 0; i <= nodenum; i++) dis[i] = INF;
vis[S] = 1, dis[S] = 0; q.push(S);
while(!q.empty()) {
int u = q.front(); q.pop();
vis[u] = 0;
for(int i = G.head(u); i; i = G.nt(i)) {
int v = G.to(i);
if(G.cap(i) && dis[v] > dis[u] + G.wt(i)) {
dis[v] = dis[u] + G.wt(i);
path[v] = i;
if(!vis[v]) {
q.push(v);
vis[v] = 1;
}
}
}
}
if(dis[T] == INF) return false;
return true;
}

inline pair<ll, ll> mcmf(int S, int T, int nodenum) {
ll flow = 0, minflow, mincost = 0;
while(SPFA(S, T, nodenum)) {
minflow = INF + 1;
for(int i = path[T]; i != -1; i = path[G.fr(i)]) {
minflow = min(minflow, G.cap(i));
}
flow += minflow;
for(int i = path[T]; i != -1; i = path[G.fr(i)]) {
G.cap(i) -= minflow;
G.cap(((i-1)^1)+1) += minflow;
}
mincost += dis[T] * minflow;
}
return make_pair(mincost, flow);
}

inline void Work() {
G.AddEdge(n + 1, 1, 0, 2);
G.AddEdge(1, n + 1, 0, 0);
G.AddEdge(n, n + 2, 0, 2);
G.AddEdge(n + 2, n, 0, 0);
printf("%lld\n", mcmf(n + 1, n + 2, n + 2).first);
}

int main() {
int T = 1;
while(T--) {
Input();
Work();
}
return 0;
}

T2 - Cyclic Tour

题目传送门

题目大意

把一个有向图分为若干个环,每个环至少包含一个节点,每个节点只能属于一个环。求这些环的边权之和和的最小值。如果没有这种分配方式,输出 -1

解题思路

注意到想要限制每个点都属于一个环,需要从入度和出度入手,所以把每个点拆为管理入度和管理出度的。这时候我们就可以通过建边尝试固定每一个点属于一个环。

从源点到每一个点建立边 $(S, i, 1,0)$,每一个点到汇点有边 $(i+n, T, 1,0)$。任意一对在原图有边相连的点都可以建立费用流边 $(i,j+n,1,w)$。其中这里边的形式表示 $(u, v, cap, cost)$。

这样如果最终最小费用最大流的最大流是满流即 $n$,就说明有一个合法的方案,输出费用即可;否则就是没有合法方案,输出 -1

正确代码

咕咕咕

T3 - Kaka’s Matrix Travels

题目传送门

题目大意

小 A 从左上角走到右下角,每个格子都有一个价值,经过这个格子就把价值拿走,每次只能往下或往右走,问你走 $k$ 次最多能拿多少价值的东西。(注意了,这里指的走 $k$ 次是指进行 $k$ 次从左上角走到右下角的操作)

解题思路

按照题目给出的顺序建图,由于每一个点有价值,所以拆点来获取它,另外要求最大价值,我们可以直接把价值取负,然后最后求出最小费用再取负就可以得到原来的答案。

然后为了处理每个点只能获得一次价值的问题我们还是建立两条边,首先设拆点后的两个节点分别是 $i$ 和 $i+n$ 然后直接建立两条边 $(i,i+n,1,a_{i,j})$ 和 $(i,i+n,k,0)$ 其中边的定义和上一题一样。这样的话意思应该很明显了。

正确代码

咕咕咕

T4 - Binary Tree on Plane

题目传送门

题目大意

在一个平面上用给出的节点画一个二叉树,其中对于任意一条合法的边,都有 $y_u>y_v$。

解题思路

找出所有合法可以建立边的点对,然后开始尝试构造网络。还是和 T2 一个思路,从出入度下手来限制每一个节点合法。不难想到对于每一个点建立 $(S, i+n, 2, 0)$ 和 $(i,T,1,0)$ 用来保证有两个儿子和一个父亲,然后对于树边建立网络边 $(i+n,j,1,dis(i,j))$ 即可。

正确代码

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
// typedef __int128 int128;
typedef pair<int , int > pii;
typedef unsigned long long ull;

namespace FastIO
{
// char buf[1 << 20], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++)
template<typename T> inline T read(T& x) {
x = 0;
int f = 1;
char ch;
while (!isdigit(ch = getchar())) if (ch == '-') f = -1;
while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
x *= f;
return x;
}
template<typename T, typename... Args> inline void read(T& x, Args &...x_) {
read(x);
read(x_...);
return;
}
inline ll read() {
ll x;
read(x);
return x;
}
};
using namespace FastIO;

template<int N, int M>
class Graph {
private :
struct Edge {
int to, nt; double wt;
ll fl, cp;
Edge() {}
Edge(int to, int nt, double wt, ll cp, ll fl) :
to(to), nt(nt), wt(wt), cp(cp), fl(fl) {}
}e[M];
int hd[N], cnte;
public :
inline void clear() { memset(hd, 0, sizeof(hd)), cnte = 0; }
inline void AddEdge(int u, int v, double w = 0, ll c = 0, ll f = 0) {
e[++cnte] = Edge(v, hd[u], w, c, f);
hd[u] = cnte;
}
inline int head(int u) { return hd[u]; }
inline int nt(int u) { return e[u].nt; }
inline int to(int u) { return e[u].to; }
inline double wt(int u) { return e[u].wt; }
inline ll& flw(int u) { return e[u].fl; }
inline ll& cap(int u) { return e[u].cp; }
};

const int MaxV = 1e4 + 10;
const int MaxE = 1e6 + 10;

int n;
pair<int , int >p[MaxV];

inline void Input() {
read(n);
for(int i = 1; i <= n; i++) {
read(p[i].first, p[i].second);
}
}

Graph< MaxV, MaxE >G;
int S, T, nv;

inline void AddEdge(int u, int v, int cap, double cst) {
G.AddEdge(u, v, cst, cap);
G.AddEdge(v, u,-cst, 0);
}

inline double Dis(int i, int j) {
return sqrt(
1.0 * (p[i].first - p[j].first) * (p[i].first - p[j].first) +
1.0 * (p[i].second - p[j].second) * (p[i].second - p[j].second)
);
}

inline void Build() {
sort(p + 1, p + n + 1, [](pair<int , int > a, pair<int , int > b) {
return a.second != b.second ? a.second > b.second : a.first < b.first;
});
S = n + n + 1, T = S + 1, nv = T;
for(int i = 1; i <= n; i++) {
AddEdge(S, i+n, 2, 0);
AddEdge(i, T, 1, 0);
for(int j = i + 1; j <= n; j++) {
if(p[i].second == p[j].second) continue;
AddEdge(i+n, j, 1, Dis(i, j));
}
}
}

int cur[MaxV];

namespace Dinic {
double ret, dis[MaxV]; int vis[MaxV];
inline bool Bfs(int S, int T, int nv) {
for(int i = 1; i <= nv; i++) dis[i] = 1e9;
dis[S] = 0, vis[S] = 1; queue<int >q; q.push(S);
while(!q.empty()) {
int u = q.front(); q.pop(); vis[u] = 0;
for(int i = G.head(u); i; i = G.nt(i)) {
int v = G.to(i);
if(G.cap(i) && dis[v] > dis[u] + G.wt(i)) {
dis[v] = dis[u] + G.wt(i);
if(!vis[v]) q.push(v), vis[v] = 1;
}
}
}
return dis[T] != 1e9;
}
inline int Dfs(int u, int cp, int T) {
if(u == T || !cp) return cp;
int tmp = 0; vis[u] = 1;
for(int &i = cur[u]; i; i = G.nt(i)) {
int v = G.to(i);
if(!vis[v] && dis[v] == dis[u] + G.wt(i)) {
int t = Dfs(v, min(1LL * cp - tmp, G.cap(i)), T);
if(!t) continue;
G.cap(i) -= t, G.cap(((i-1)^1)+1) += t;
tmp += t, ret += t * G.wt(i);
if(tmp == cp) break;
}
}
vis[u] = 0;
return tmp;
}
};

inline pair<int , double > GetMCMF() {
int mf = 0; Dinic::ret = 0;
while(Dinic::Bfs(S, T, nv)) {
for(int i = 1; i <= nv; i++) cur[i] = G.head(i);
int x; while(x = Dinic::Dfs(S, 1e9, T)) mf += x;
}
return make_pair(mf, Dinic::ret);
}

inline void Work() {
Build();
if(p[1].second == p[2].second) {
printf("-1");
return ;
}
pair< int , double >ans = GetMCMF();
// cout << ans.first << ' ' << ans.second << endl;
if(ans.first != n - 1) printf("-1");
else printf("%.7lf", ans.second);
}

int main() {
int T = 1;
while(T--) {
Input();
Work();
}
return 0;
}

T5 - Lunch Time

题目传送门

题目大意

有一张 $n$ 个点 $m$ 条边的图,$k$ 个人从 $1$ 出发去往 $n$。每条边有一个宽度 $c_i$ 表示最多同时有 $c_i$ 个人通过这条道路,花费时间为1,求所有人都到达终点的最短时间。

解题思路

正常跑费用流,每找到一个可行的增广路就计算一次最短时间

正确代码

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
// typedef __int128 int128;
typedef pair<int , int > pii;
typedef unsigned long long ull;

namespace FastIO
{
// char buf[1 << 20], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++)
template<typename T> inline T read(T& x) {
x = 0;
int f = 1;
char ch;
while (!isdigit(ch = getchar())) if (ch == '-') f = -1;
while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
x *= f;
return x;
}
template<typename T, typename... Args> inline void read(T& x, Args &...x_) {
read(x);
read(x_...);
return;
}
inline ll read() {
ll x;
read(x);
return x;
}
};
using namespace FastIO;

template<int N, int M>
class Graph {
private :
struct Edge {
int to, nt;
ll wt, fl, cp;
Edge() {}
Edge(int to, int nt, ll wt, ll cp, ll fl) :
to(to), nt(nt), wt(wt), cp(cp), fl(fl) {}
}e[M];
int hd[N], cnte;
public :
inline void clear() { memset(hd, 0, sizeof(hd)), cnte = 0; }
inline void AddEdge(int u, int v, ll w = 0, ll c = 0, ll f = 0) {
e[++cnte] = Edge(v, hd[u], w, c, f);
hd[u] = cnte;
}
inline int head(int u) { return hd[u]; }
inline int nt(int u) { return e[u].nt; }
inline int to(int u) { return e[u].to; }
inline ll wt(int u) { return e[u].wt; }
inline ll& flw(int u) { return e[u].fl; }
inline ll& cap(int u) { return e[u].cp; }
};

const int MaxV = 5010;
const int MaxE = 2e4 + 10;

int n, m, K;
Graph< MaxV, MaxE >G;

int S, T, nv;

inline void Input() {
S = 1, T = n, nv = n;
int u, v, w;
for(int i = 1; i <= m; i++) {
read(u, v, w);
u++, v++;
G.AddEdge(u, v, 1, w);
G.AddEdge(v, u,-1, 0);
}
}

int cur[MaxV];

namespace Dinic {
int ret, dis[MaxV], vis[MaxV];
inline bool Bfs(int S, int T, int nv) {
for(int i = 1; i <= nv; i++) dis[i] = 1e9;
dis[S] = 0, vis[S] = 1; queue<int >q; q.push(S);
while(!q.empty()) {
int u = q.front(); q.pop(); vis[u] = 0;
for(int i = G.head(u); i; i = G.nt(i)) {
int v = G.to(i);
if(G.cap(i) && dis[v] > dis[u] + G.wt(i)) {
dis[v] = dis[u] + G.wt(i);
if(!vis[v]) q.push(v), vis[v] = 1;
}
}
}
return dis[T] != 1e9;
}
inline int Dfs(int u, int cp, int T) {
if(u == T || !cp) return cp;
int tmp = 0; vis[u] = 1;
for(int &i = cur[u]; i; i = G.nt(i)) {
int v = G.to(i);
if(!vis[v] && dis[v] == dis[u] + G.wt(i)) {
int t = Dfs(v, min(1LL * cp - tmp, G.cap(i)), T);
if(!t) continue;
G.cap(i) -= t, G.cap(((i-1)^1)+1) += t;
tmp += t, ret += t * G.wt(i);
if(tmp == cp) break;
}
}
vis[u] = 0;
return tmp;
}
};

inline int GetMCMF() {
if(K == 0) return 0;
int mf; Dinic::ret = 0; int ans = 1e9;
int sum = K, sum2 = 0; pair<int , int >lst;
while(Dinic::Bfs(S, T, nv)) {
for(int i = 1; i <= nv; i++) cur[i] = G.head(i);
int tmp = Dinic::Dfs(S, 1e9, T);
mf += tmp;

pair<int , int>nw = make_pair(Dinic::dis[T], tmp);
sum -= (nw.first - lst.first) * sum2 + nw.second; sum2 += nw.second;
ans = min(ans, nw.first + (int)ceil(1.0 * ((sum < 0) ? 0 : sum) / sum2));
lst = nw; if(sum <= 0) break;
}
return ans;
}

inline void Work() {
int ans = GetMCMF();
if(ans == 1e9) printf("No solution\n");
else printf("%d\n", ans);
}

inline void Clear() {
G.clear();
}

int main() {
int T = 1;
while(~scanf("%d%d%d", &n, &m, &K)) {
Input();
Work();
Clear();
}
return 0;
}