CF1517G
鉴于 $|x_j'-x|,|y_j'-y|\leq 1$,我们大胆尝试,画出所有可能的平行四边形的形状。对于任意一个合法的四边形(即满足题面中两个条件的平行四边形),会发现如下性质:
-
它的四个端点在 $x,y$ 轴坐标的奇偶性各不相同(不相同当且仅当 $x$ 不同或者 $y$ 不同)。
这个性质将会方便我们进行图论建模。
-
进一步的,有:任意一个合法的平行四边形,它的四个端点可以通过以下方式连成一条四联通的道路:
$x$ 奇数 $y$ 奇数 $\to$ $x$ 偶数 $y$ 奇数 $\to$ $x$ 偶数 $y$ 偶数 $\to$ $x$ 奇数 $y$ 偶数。
-
于是我们利用性质简化题目:删掉权重之和最小的点集,使得不存在上述的四联通道路。
由于路径中依次经过的点 $x,y$ 奇偶性不同,考虑建出分层图后最小割。
不妨钦定路径依次经过的四类点为 $o1,o2,o3,o4$。那么我们拿源点连向 $o1$ 中的所有点,再用 $o1$ 的点连向 $o2$ 中和它四联通的点……最后让 $o4$ 中的所有点连向汇点。上述的边我们是不希望看到它们被割掉的,故而权值都设为 $+\infty$。
可是路径的权值该怎么定?设在连接两层的边上不太妥当。考虑将一层中的一个点 $u$ 拆成两个,他们之间连一条边,边权即为 $u$ 的权重。
#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 N = 2010, M = 253010;
const ll INF = 1e18;
struct E{int v; ll w, nxt;} e[M << 1]; ll ans;
int n, s, t, x[N], y[N], w[N], o[N], tote, head[N], now[N], dep[N];
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 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", &n), t = n * 2 + 1;
memset(head, -1, sizeof(head));
FL(i, 1, n){
scanf("%d%d%d", &x[i], &y[i], &w[i]);
ans += w[i], Add(i, n + i, w[i]);
if((x[i] & 1) && (y[i] & 1)) o[i] = 1, Add(s, i, INF);
else if(!(x[i] & 1) && (y[i] & 1)) o[i] = 2;
else if(!(x[i] & 1) && !(y[i] & 1)) o[i] = 3;
else o[i] = 4, Add(n + i, t, INF);
}
FL(i, 1, n) FL(j, 1, n) if(o[i] + 1 == o[j]){
if(abs(x[i] - x[j]) + abs(y[i] - y[j]) == 1)
Add(n + i, j, INF);
}
printf("%lld\n", ans - dinic());
return 0;
}