BZOJ 2521: [Shoi2010]最小生成树(最小割)

题意

对于某一条无向图中的指定边 ((a, b)) , 求出至少需要多少次操作。可以保证 ((a, b)) 边在这个无向图的最小生成树中.

一次操作指: 先选择一条图中的边 ((u, v)), 再把图中除了这条边以外的边, 每一条的权值都减少 (1) .

(n le 500, m le 800, 1 le w_i < 10^6)

题解

给除了一条边的所有边权 (-1) ,相当于给这条边的边权 (+1)

利用生成树的结论,一条边 (u o v) (权值 (w) )若要必定存在于生成树中,那么 (u o v) 所有路径上存在一条边的边权都不能大于 (w)

那么我们令这些边的权值为 (w_i' = max {w(a,b)-w_i+1, 0})

然后从 (a o b) 跑一遍最小割就行了。

这样为什么是对的呢?因为只要割掉了所有可行的边,那么就把 (a, b) 分成两个连通子图,要连上这两个子图的最小代价就是这个最小割的答案。

总结

对于一些奇怪数据范围的题,可以向网络流上想。

代码

注意连边的时候,需要把正向边和反向边的流量都置为正,这是因为整个图是无向的。

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << x << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)

using namespace std;

inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}

inline int read() {
    int x = 0, fh = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
    for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
    return x * fh;
}

void File() {
#ifdef zjp_shadow
	freopen ("2521.in", "r", stdin);
	freopen ("2521.out", "w", stdout);
#endif
}

const int N = 510, M = 810 * 2, inf = 0x3f3f3f3f;

int n, m, id;

namespace Dinic {

	int Head[N], Next[M], to[M], cap[M], e = 1;

	inline void add_edge(int u, int v, int flow) { to[++ e] = v; Next[e] = Head[u]; Head[u] = e; cap[e] = flow; }

	inline void Add(int u, int v, int flow) { add_edge(u, v, flow); add_edge(v, u, flow); }

	int S, T, dis[N];
	bool Bfs() {
		queue<int> Q; Set(dis, 0); Q.push(S); dis[S] = 1;
		while (!Q.empty()) {
			int u = Q.front(); Q.pop();
			for (int i = Head[u]; i; i = Next[i]) if (cap[i]) {
				int v = to[i]; if (!dis[v]) { dis[v] = dis[u] + 1; if (v == T) return true; Q.push(v); }
			}
		}
		return false;
	}

	int cur[N];
	int Dfs(int u, int flow) {
		if (u == T || !flow) return flow;
		int res = 0, f;
		for (int &i = cur[u]; i; i = Next[i]) if (cap[i]) {
			int v = to[i]; if (dis[v] != dis[u] + 1) continue ;
			if ((f = Dfs(v, min(flow, cap[i])))) {
				cap[i] -= f; cap[i ^ 1] += f;
				res += f; if (!(flow -= f)) break;
			}
		}
		if (!flow || !res) dis[u] = 0;
		return res;
	}

	int Run() {
		int sum_flow = 0;
		while (Bfs()) { Cpy(cur, Head); sum_flow += Dfs(S, inf); }
		return sum_flow;
	}

}

using namespace Dinic;

struct Edge { int u, v, w; } lt[M];

int main () {

	File();

	n = read(); m = read(); id = read();
	For (i, 1, m) {
		int u = read(), v = read(), w = read();
		lt[i] = (Edge){u, v, w};
	}
	
	S = lt[id].u; T = lt[id].v;
	For (i, 1, m) if (id != i) {
		int res = max(lt[id].w - lt[i].w + 1, 0);
		if (res) Add(lt[i].u, lt[i].v, res);
	}

	printf ("%d
", Run());

    return 0;
}
原文地址:https://www.cnblogs.com/zjp-shadow/p/9556688.html