do_while_true

一言(ヒトコト)

「题解」Codeforces 311E Biologist

我写的第一道最小割¿¿¿

二选一,考虑一个鱼刺型建图(自己编的名字),然后用最小割求最小花费。

鱼刺性建图大概就是,中间有一排点,然后位于左侧的 \(S\) 连向这一排点,这一排点连向右侧的 \(T\),看起来就很像鱼刺(?)

trick:最大价值=总价值-最小花费

如果这个位置是 \(0\),那么源点 \(S\) 连到它一条流量为 \(v\) 的边,若存在则代表选 \(0\),若割掉则代表花费了 \(v\) 的代价选为 \(1\);如果这个位置是 \(1\),那么它连到汇点 \(T\) 一条流量为 \(v\) 的边,若存在则代表选 \(1\),若割掉则代表花费了 \(v\) 的代价选为 \(0\)

首先这个 \(g\) 是没有用的,就如果当二五仔要赔钱的话,把 \(W\) 加上 \(g\),然后最后总答案再减去 \(g\) 即可。

现在这个限制是:如果这些位置你都选 \(0/1\),就可以喜提 \(W\) 的价值。对应到最小割上:先假定满足条件,获得 \(W\) 的价值;如果这些位置的 \(1/0\) 点与 \(T/S\) 边有一条没被割掉,那么就需要付出 \(W\) 的代价。

那么建一个新点 \(x\),如果它限制的位置都选 \(0\),就连 \((x,\text{限制的位置},+\infty)\)\((S,x,W)\),也就是 \(x\) 在二分图中和 \(0\) 点位于一部分。如果它限制的位置都选 \(1\) 也同理,只不过它在二分图中和 \(1\) 点位于一部分。

感性理解一下为什么这样子的最小割是最小花费:假设它限制若干位置都选 \(0\),如果某些位置本来填的是 \(1\),那么如果那个位置连向 \(T\) 的边没有被割掉,不符合限制,\((S,x,W)\) 一定要被割掉,符合题意;如果某些位置本来填的是 \(0\),如果这个位置 \(p\) 对应的边被割掉了,在最小割中,那么从它开始一定至少可以到达一个没有被割掉的限制 \(y\),这样流可以从 \(S\to x\to p\to y\to T\) 流过,在这一路中,只有 \(S\to x\) 的边是可以割的(若割掉所有的 \(y\to T\),那么 \(S\to p\) 一定不会割掉,因为割掉的话就不优了),所以 \((S,x,W)\) 一定要割掉,符合题意。

至于可能出现 \(0\) 流量的边,其实是无关紧要的(?)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#define pb emplace_back
#define mp std::make_pair
#define fi first
#define se second
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
typedef std::vector<int> vi;
const ll mod = 998244353;
ll Add(ll x, ll y) { return (x+y>=mod) ? (x+y-mod) : (x+y); }
ll Mul(ll x, ll y) { return x * y % mod; }
ll Mod(ll x) { return x < 0 ? (x + mod) : (x >= mod ? (x-mod) : x); }
ll cadd(ll &x, ll y) { return x = (x+y>=mod) ? (x+y-mod) : (x+y); }
ll cmul(ll &x, ll y) { return x = x * y % mod; }
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template<typename T, typename... T2> T Max(T x, T2 ...y) { return Max(x, y...); }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template<typename T, typename... T2> T Min(T x, T2 ...y) { return Min(x, y...); }
template <typename T> T cmax(T &x, T y) { return x = x > y ? x : y; }
template <typename T> T cmin(T &x, T y) { return x = x < y ? x : y; }
template <typename T>
T &read(T &r) {
	r = 0; bool w = 0; char ch = getchar();
	while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
	while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
	return r = w ? -r : r;
}
template<typename T1, typename... T2>
void read(T1 &x, T2& ...y) { read(x); read(y...); }
const int N = 100010;
const int M = 500010;
const ll INF = 0x7fffffffffffffff;
int n, m, g;
int a[N], v[N];
int tot, S, T, ent = 1, head[N], cur[N], dis[N];
struct Edge {
	int to, nxt;
	ll fl;
}e[M << 1];
inline void add(int x, int y, ll z) {
	//printf("%d %d %d\n", x, y, z);
	e[++ent].to = y; e[ent].fl = z; e[ent].nxt = head[x]; head[x] = ent;
	e[++ent].to = x; e[ent].fl = 0; e[ent].nxt = head[y]; head[y] = ent;
}
bool bfs() {
	for(int i = 1; i <= tot; ++i) dis[i] = -1, cur[i] = head[i];
	std::queue<int>q;
	q.push(S); dis[S] = 0;
	while(!q.empty()) {
		int x = q.front(); q.pop();
		for(int i = head[x]; i; i = e[i].nxt) {
			int v = e[i].to;
			if(dis[v] == -1 && e[i].fl) {
				dis[v] = dis[x] + 1;
				q.push(v);
			}
		}
	}
	return dis[T] != -1;
}
ll dfs(int x, ll lim) {
	if(x == T) return lim;
	ll flow = 0;
	for(int i = cur[x]; i && flow < lim; i = e[i].nxt) {
		int v = e[i].to; cur[x] = i;
		if(dis[v] == dis[x] + 1 && e[i].fl) {
			ll f = dfs(v, Min(e[i].fl, lim - flow));
			flow += f; e[i].fl -= f; e[i^1].fl += f;
		}
	}
	return flow;
}
ll dinic() {
	ll mxfl = 0;
	while(bfs())
		mxfl += dfs(S, INF);
	return mxfl;
}
signed main() { //freopen("data.in", "r", stdin);
	ll sum = 0;
	read(n, m, g); tot = n; S = ++tot; T = ++tot;
	for(int i = 1; i <= n; ++i) read(a[i]);
	for(int i = 1; i <= n; ++i) read(v[i]);
	for(int i = 1; i <= n; ++i) {
		if(!a[i]) add(S, i, v[i]);
		else add(i, T, v[i]);
	}
	for(int i = 1; i <= m; ++i) {
		int p = ++tot, to, W, k, f; read(to, W, k); vi vec;
		for(int j = 1, x; j <= k; ++j) {
			read(x);
			if(!to) add(p, x, INF);
			else add(x, p, INF);
		}
		read(f);
		if(f) W += g, sum -= g;
		sum += W;
		if(!to) add(S, p, W);
		else add(p, T, W);
	}
	printf("%lld\n", sum - dinic());
	return 0;
}
posted @ 2022-01-17 22:11  do_while_true  阅读(32)  评论(0编辑  收藏  举报