P1935 [国家集训队] 圈地计划
先看一道较为类似的题:文理分科。
在那道题里,我们的最小割模型中有以下几种边:
-
$S\to id(i,j)$,边权为 $art_{i,j}$
其中,$id(i,j)$ 表示在第 $i$ 行第 $j$ 列的人。这条边保留表示在这个人选文科;割掉则是选理科。
-
$id(i,j)\to T$,边权为 $science_i$。
这条边保留表示当前这个人选理科;反之选文科。
-
对于一个人及其上下左右的组合 $k$,我们建立虚点 $num1_k,num2_k$
-
$s\to num1_k$,边权为 $same_art$;以及 $num1_k\to id(i,j)$,边权为 $\text{INF}$(满足$\{i,j\}\in k$)。
-
$num2_k\to t$,边权为 $same_art$;以及 $id(i,j)\to num2_k$,边权为 $\text{INF}$(满足$\{i,j\}\in k$)。
-
回归这道题。我们不妨把那个 $k\times C_{i,j}$ 拆成几个 $C_{i,j}$ 相加,之后就只要考虑一个位置与它某个相邻位置的独立贡献了。之后就是上述文理分可经典模型。
但是还有个不同点:这题要求两个位置不一样才有贡献——考虑对棋盘黑白染色,对于黑点,我们还是按照 $(S\to id(i,j),art_{i,j}),(id(i,j)\to T,science_{i,j})$ 的方式连边;对于白点,我们反一下,$(S\to id(i,j),science_{i,j}),(id(i,j)\to T,art_{i,j})$。问题得到解决。
#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); i++)
#define FR(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
typedef long long ll;
const int S = 110, N = 100010, M = 260010;
const int f[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
const ll INF = 1e18;
struct E{int v; ll w, nxt;} e[M << 1];
int n, m, s, t, tot, tote; ll ans;
int a[S][S], b[S][S], head[N], now[N], dep[N];
void init(){
tote = 0;
memset(head, -1, sizeof(head));
}
void Adde(int u, int v, ll w){
e[tote] = {v, w, head[u]}, head[u] = tote++;
}
void Add(int u, int v, ll w){
Adde(u, v, w), Adde(v, u, 0);
}
int id(int x, int y){
return (x - 1) * m + y;
}
int bfs(){
queue<int> q; q.push(s);
memset(dep, 0, sizeof(dep)), dep[s] = 1;
while(!q.empty()){
int u = q.front(); q.pop();
for(int i = now[u] = head[u]; ~i; i = e[i].nxt)
if(e[i].w && !dep[e[i].v])
q.push(e[i].v), dep[e[i].v] = dep[u] + 1;
}
return dep[t];
}
ll dfs(int u, ll in){
if(u == t) return in; ll out = 0;
for(int i = now[u]; ~i && in; i = e[i].nxt){
int v = e[i].v; ll w = e[i].w; now[u] = i;
if(dep[v] == dep[u] + 1 && w){
ll flow = dfs(v, min(in, (ll)w));
in -= flow, out += flow;
e[i].w -= flow, e[i ^ 1].w += flow;
}
}
if(!out) dep[u] = 0; return out;
}
ll dinic(){
ll ret = 0;
while(bfs()) ret += dfs(s, INF);
return ret;
}
int main(){
scanf("%d%d", &n, &m);
init(), s = 0, t = ++(tot = n * m);
FL(i, 1, n) FL(j, 1, m){
scanf("%d", &a[i][j]), ans += a[i][j];
if((i + j) & 1) Add(s, id(i, j), a[i][j]);
else Add(id(i, j), t, a[i][j]);
}
FL(i, 1, n) FL(j, 1, m){
scanf("%d", &b[i][j]), ans += b[i][j];
if((i + j) & 1) Add(id(i, j), t, b[i][j]);
else Add(s, id(i, j), b[i][j]);
}
FL(i, 1, n) FL(j, 1, m){
int val; scanf("%d", &val);
FL(k, 0, 3){
int x = i + f[k][0], y = j + f[k][1];
if(x < 1 || x > n || y < 1 || y > m) continue;
ans += val, Add(s, ++tot, val);
Add(tot, id(i, j), INF), Add(tot, id(x, y), INF);
ans += val, Add(++tot, t, val);
Add(id(i, j), tot, INF), Add(id(x, y), tot, INF);
}
}
printf("%lld\n", ans - dinic());
return 0;
}