do_while_true

一言(ヒトコト)

「题解」洛谷 P1935 [国家集训队]圈地计划

还是那个经典trick:最大价值=总价值-最小花费

每个位置都是二选一,考虑一个鱼刺型建图。

然后就是需要描述一个,如果 \(x\) 选了第 \(p\) 种方案,那么如果它的邻点也选了第 \(p\) 种方案,就有 \(-C\) 的代价,也就是有一条 \(-C\) 流量的边需要割掉。

但是如果直接 \(S\) 连向每个格子的边流量为 \(A\),每个格子流向 \(T\) 的格子流量为 \(B\),你会发现这个限制描述不了。

注意到网格图四联通实际上是个二分图,按照格子的横纵坐标和的奇偶性分类就是个二分图。

那么将横纵坐标和为偶数的格子,连边变成 \((S,x,B),(x,T,A)\),然后限制的话直接在邻点之间连 \((x,y,C_x+C_y),(y,x,C_x+C_y)\) 流量的边,这样如果 \(x\) 和其邻点 \(y\) 如果选择了同样的一种方案,那么就需要付出 \(C_x+C_y\) 的代价。

这样用总价值减去最小割就可以了。

#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 = 5010;
const int M = 10010;
const ll INF = 0x7fffffffffffffff;
int n, m, A[110][110], B[110][110], C[110][110], p[110][110];
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, int 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() {
	read(n, m); S = ++tot; T = ++tot;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			p[i][j] = ++tot;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			read(A[i][j]);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			read(B[i][j]);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			read(C[i][j]);
	ll sum = 0;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j) {
			if((i + j) & 1) {
				add(S, p[i][j], A[i][j]);
				add(p[i][j], T, B[i][j]);
			}
			else {
				add(S, p[i][j], B[i][j]);
				add(p[i][j], T, A[i][j]);
			}
			sum += A[i][j] + B[i][j];
			if(p[i-1][j]) {
				add(p[i-1][j], p[i][j], C[i][j] + C[i-1][j]);
				sum += C[i][j]; 
			}
			if(p[i+1][j]) {
				add(p[i+1][j], p[i][j], C[i][j] + C[i+1][j]);
				sum += C[i][j];
			}
			if(p[i][j-1]) {
				add(p[i][j-1], p[i][j], C[i][j] + C[i][j-1]);
				sum += C[i][j]; 
			}
			if(p[i][j+1]) {
				add(p[i][j+1], p[i][j], C[i][j] + C[i][j+1]);
				sum += C[i][j]; 
			}
		}
	printf("%lld\n", sum - dinic());
	return 0;
}
posted @ 2022-01-17 22:15  do_while_true  阅读(32)  评论(0编辑  收藏  举报