Description
给定一个 n n n 点 m m m 边的有向网络图,求 s s s 到 t t t 的最大流.
Limitations
1
≤
n
≤
1200
1\le n\le 1200
1≤n≤1200
1
≤
m
≤
1.2
×
1
0
5
\textcolor{red}{1\le m\le 1.2\times 10^5}
1≤m≤1.2×105
1
≤
u
,
v
,
s
,
t
≤
n
1\le u,v,s,t\le n
1≤u,v,s,t≤n
1
≤
w
<
2
31
1\le w<2^{31}
1≤w<231
1.5
s
,
500
MB
(on
luogu)
1.5\text{s},500\text{MB}\;\texttt{(on luogu)}
1.5s,500MB(on luogu)
Solution
其实不需要 HLPP
.
Dinic
在边权差距小的时候跑得较快,所以我们进行分组,对每个
i
∈
[
0
,
31
)
i\in[0,31)
i∈[0,31),将
w
∈
[
2
i
,
2
i
+
1
)
w\in[2^i,2^{i+1})
w∈[2i,2i+1) 的边分为一组,从大到小对,对每组分别跑 Dinic
,可以显著加快速度.(跑完的边和下一组一起跑)
但这样还不够,注意到反向边会拖慢 Dinic
速度(因为会退回输送过去的流量).
对于每组,我们可以先只加正向边跑一遍 Dinic
(但是要更新反向边的流量).
然后在残量网络上一次性加入所有反向边,再跑一遍 Dinic
即可求出正确答案.
效率再次提升,可以通过此题,时间复杂度玄学.
Code
3.69
KB
,
2.77
s
,
5.15
MB
(in
total,
C++20
with
O2)
3.69\text{KB},2.77\text{s},5.15\text{MB}\;\texttt{(in total, C++20 with O2)}
3.69KB,2.77s,5.15MB(in total, C++20 with O2)(on luogu)
最大流板子需要修改一处(先不加反向边),板子其他部分省略.
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;
template<class T>
bool chmax(T &a, const T &b){
if(a < b){ a = b; return true; }
return false;
}
template<class T>
bool chmin(T &a, const T &b){
if(a > b){ a = b; return true; }
return false;
}
template<class T>
struct MaxFlow {
// ......
void addEdge(int u, int v, T c) {
g[u].push_back(e.size());
e.emplace_back(v, c);
// g[v].push_back(e.size()); // 暂时不加反向边
e.emplace_back(u, 0);
}
// ......
};
using Edge = array<int, 3>;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m, s, t;
scanf("%d %d %d %d", &n, &m, &s, &t), s--, t--;
vector<Edge> e(m);
for (auto & [u, v, w] : e) scanf("%d %d %d", &u, &v, &w), u--, v--;
sort(e.begin(), e.end(), [&](const Edge& a, const Edge& b) { return a[2] > b[2]; });
MaxFlow<int> g(n);
int ans = 0;
for (int tp : {0, 1})
for (int p = 1 << 30, i = 0; p; p /= 2) {
while (i < m && e[i][2] >= p) {
auto [u, v, w] = e[i];
if (tp == 1) g.g[v].push_back(i * 2 + 1);
else g.addEdge(u, v, w);
i++;
}
ans += g.flow(s, t);
}
printf("%d\n", ans);
return 0;
}