将点按照 (x) 坐标的奇偶性,(y) 坐标的奇偶性分为 (4) 类,可以发现如下性质:
- 每一个不满足题意的平行四边形都包含了 (4) 类点恰好一个。
- 每一个不满足题意的平行四边形都可以表示为 ( ext{(odd, odd)} o ext{(even, odd)} o ext{(even, even)} o ext{(odd, even)}) 的一条道路。
于是题目转化为,丢掉总权值最小的点,使得不存在这样的道路,这是一个最小割模型。
具体的,删点的话可以考虑拆点,将每个点 (u) 表示为 (u_{in}, u_{out}) 两个点。
根据路径经过的顺序,将上面那 (4) 类点分为 (A, B, C, D),同时建立源点 (S) 和汇点 (T)。
图为:
- (u_{in} o u_{out}) 连容量为 (w_u) 的边。
- (S o a_{in} (a in A)) 连容量为 (+infty) 的边。
- (a_{out} o b_{in} (a in A, b in B, ext{dist}(a, b) = 1)) 连容量为 (+infty) 的边。
- (b_{out} o c_{in} (b in B, c in C, ext{dist}(b, c) = 1)) 连容量为 (+infty) 的边。
- (c_{out} o d_{in} (c in C, d in D, ext{dist}(c, d) = 1)) 连容量为 (+infty) 的边。
- (d_{out} o T (d in D)) 连容量为 (+infty) 的边。
求最小割即可,边数和点数都是 (O(n)) 级别的。
#include <bits/stdc++.h>
#define int long long
#define Get(x) ((x % 2 + 2) % 2)
using namespace std;
const int N = 2005, M = 10005;
struct edge
{
int v, w, nxt;
} e[M << 1];
int cnt = -1, n, m, s, t, maxflow, h[N], lev[N], cur[N];
inline void AddEdge(int u, int v, int w)
{
e[++cnt] = (edge){v, w, h[u]}; h[u] = cnt;
e[++cnt] = (edge){u, 0, h[v]}; h[v] = cnt;
}
int DFS(int u, int canflow)
{
if(u == t) return canflow;
int resflow = 0;
for(int& i = cur[u]; ~i; i = e[i].nxt)
{
int v = e[i].v;
if(lev[v] == lev[u] + 1 && e[i].w > 0)
{
int willflow = DFS(v, min(e[i].w, canflow));
canflow -= willflow;
resflow += willflow;
e[i].w -= willflow;
e[i ^ 1].w += willflow;
if(!canflow) break;
}
}
if(!resflow) lev[u] = -1;
return resflow;
}
bool BFS()
{
memset(lev, -1, sizeof lev);
queue<int> que;
que.push(t);
lev[t] = 998244353;
while(!que.empty())
{
int u = que.front(); que.pop();
for(int i = h[u]; ~i; i = e[i].nxt)
{
int v = e[i].v;
if(lev[v] != -1 || e[i ^ 1].w <= 0) continue;
lev[v] = lev[u] - 1;
if(v == s) return true;
que.push(v);
}
}
return lev[s] != -1;
}
void Dinic()
{
while(BFS())
{
memcpy(cur, h, sizeof h);
maxflow += DFS(s, INT_MAX);
}
}
int x[N], y[N], w[N], type[N], sum;
signed main()
{
ios::sync_with_stdio(false);
memset(h, -1, sizeof h);
cin >> n;
for(int i = 1; i <= n; i++) { cin >> x[i] >> y[i] >> w[i]; sum += w[i]; }
s = 1; t = n * 2 + 2;
for(int i = 1; i <= n; i++)
{
if(Get(x[i]) == 1 && Get(y[i]) == 1) type[i] = 1;
if(Get(x[i]) == 0 && Get(y[i]) == 1) type[i] = 2;
if(Get(x[i]) == 0 && Get(y[i]) == 0) type[i] = 3;
if(Get(x[i]) == 1 && Get(y[i]) == 0) type[i] = 4;
}
for(int i = 1; i <= n; i++) AddEdge(i << 1, i << 1 | 1, w[i]);
for(int i = 1; i <= n; i++) if(type[i] == 1) AddEdge(s, i << 1, LLONG_MAX / 64);
for(int i = 1; i <= n; i++) if(type[i] == 1)
for(int j = 1; j <= n; j++) if(type[j] == 2)
if(abs(x[i] - x[j]) + abs(y[i] - y[j]) == 1)
AddEdge(i << 1 | 1, j << 1, LLONG_MAX / 64);
for(int i = 1; i <= n; i++) if(type[i] == 2)
for(int j = 1; j <= n; j++) if(type[j] == 3)
if(abs(x[i] - x[j]) + abs(y[i] - y[j]) == 1)
AddEdge(i << 1 | 1, j << 1, LLONG_MAX / 64);
for(int i = 1; i <= n; i++) if(type[i] == 3)
for(int j = 1; j <= n; j++) if(type[j] == 4)
if(abs(x[i] - x[j]) + abs(y[i] - y[j]) == 1)
AddEdge(i << 1 | 1, j << 1, LLONG_MAX / 64);
for(int i = 1; i <= n; i++) if(type[i] == 4) AddEdge(i << 1 | 1, t, LLONG_MAX / 64);
Dinic();
cout << sum - maxflow << endl;
return 0;
}