LG P4722 & LOJ 127 【模板】最大流 加强版 Solution

Description

给定一个 n n n m m m 边的有向网络图,求 s s s t t t 的最大流.

Limitations

1 ≤ n ≤ 1200 1\le n\le 1200 1n1200
1 ≤ m ≤ 1.2 × 1 0 5 \textcolor{red}{1\le m\le 1.2\times 10^5} 1m1.2×105
1 ≤ u , v , s , t ≤ n 1\le u,v,s,t\le n 1u,v,s,tn
1 ≤ w < 2 31 1\le w<2^{31} 1w<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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值