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;
}
posted @ 2023-08-19 14:24  徐子洋  阅读(19)  评论(0)    收藏  举报  来源