「题解」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;
}