题解:AT_arc122_f [ARC122F] Domination

posted on 2025-04-30 07:16:55 | under | source

题意:坐标系上有 \(n\) 个红点,\(m\) 个蓝点,蓝点可以随意移动,代价为曼哈顿距离。求最小代价使得每个红点右上方有至少 \(k\) 个蓝点。\(n,m\le 10^5,k\le 10\)

先做一步转化,对于红点只需保留一个“上凸包”,因为凸包下面的节点的限制必然更松。也就是说,目前的红点满足 \(x\) 单增,\(y\) 单减。

然后我们假定蓝点不可移动,此时蓝点覆盖的红点构成一段区间。有经典结论:合法当且仅当可以将区间划分为 \(k\) 组,使得每一组都将所有点覆盖一遍。

其中,“所有点被覆盖一遍”是可以建图的,只需让 \(l\to r+1\) 连边,同时建立 \(i\to i-1\) 的链,那么条件显然是存在 \(1\to n+1\) 路径。

在此基础上,考虑怎么刻画蓝点的移动,实际上区间两端点是独立的,所以把蓝点作为中转点分别向两端连边。具体来说,让每个蓝点 \(i\) 被所有红点 \(j\) 连边 \(j\to i\) 代价为 \(\max(0,Ry_j-By_i)\),以及 \(i\to j\) 代价为 \(\max(0,Rx_{j-1}-Bx_i)\)。然后跑最短路即可解决 \(k=1\) 的问题。

\(k\) 更大的情况使用费用流即可,将蓝点的边设置流量为 \(1\) 表示只能归为一组,其它流量都是 \(k\)

接下来降低边数,容易想到将 \(x,y\) 轴拆开来。把所有点分别按到 \(x,y\) 轴:

  • 需要注意的是,\(y\) 轴上第 \(i\) 个红点对应实际的第 \(i+1\) 个红点。\(1\) 号红点呢?它是没用的,因为相当于原地绕圈。具体见下文。
  • 刻画蓝点的移动:先让蓝点对应 \(y\) 轴上点向 \(x\) 轴上点连边,流量为 \(1\),无需代价;然后允许在 \(x\) 轴向右移动、在 \(y\) 轴向下移动,但是花费代价。那么将 \([l,r]\) 拓展为 \([L,R]\),对应路径 \(L\to l\to r\to R\)
  • 刻画在红点链上移动:让 \(x\) 轴的点向左连边、\(y\) 轴向上连边;同时对于 \(i\in [1,n)\),让红点 \(i\) 对应的 \(x\)\(y\) 连边。流量均 \(k\),无需代价。显然是对的。
  • 最后,源点向 \(y\) 轴最上面的红点连边,\(x\) 轴最右边的红点向汇点连边。

现在图的规模变为 \(O(n+m)\),跑 dijskstra 费用流,复杂度 \(O(k(n+m)\log (n+m))\)

代码

#include<bits/stdc++.h>
using namespace std;

#define LL long long
#define pir pair<LL, int>
const int N = 1e5 + 5, M = 2e6 + 5;
const LL inf = 1e18;
int _n, n, m, k, ix, iy;
struct node{int x, y;} _r[N], r[N], b[N]; 
map<int, int> idx, idy;

inline bool cmp(node A, node B) {return A.x == B.x ? A.y > B.y : A.x > B.x;}

namespace MCMF{
    int n, tot = 1, head[N << 1], S, T, pre[N << 1];
    LL dis[N << 1], h[N << 1];
    bool vis[N << 1];
    struct edge{int u, v, nxt; LL c, f;} e[M];
    
    inline void add(int u, int v, LL c, LL f) {e[++tot] = {u, v, head[u], c, f}, head[u] = tot;}
    inline void span(int u, int v, LL c, LL f) {add(u, v, c, f), add(v, u, -c, 0);}
    inline LL mcmf(){
        for(int i = 1; i <= n; ++i) dis[i] = inf;
        LL ans = 0; int ff = 0;
        while(1){
            priority_queue<pir, vector<pir>, greater<pir> > q;
            q.push({dis[S] = 0, S});
            while(!q.empty()){
                int u = q.top().second; q.pop();
                if(vis[u]) continue; vis[u] = true;
                for(int i = head[u]; i; i = e[i].nxt){
                    if(!e[i].f) continue;
                    int v = e[i].v, w = e[i].c + h[u] - h[v];
                    if(dis[v] > dis[u] + w){
                        dis[v] = dis[u] + w, pre[v] = i;
                        q.push({dis[v], v});
                    }
                } 
            }
            if(dis[T] == inf) break;
            LL mif = inf, cs = 0;
            for(int i = T; i != S; i = e[pre[i]].u) mif = min(mif, e[pre[i]].f), cs += e[pre[i]].c;
            ans += mif * cs;
            ff += mif;
            for(int i = T; i != S; i = e[pre[i]].u) e[pre[i]].f -= mif, e[pre[i] ^ 1].f += mif;
            for(int i = 1; i <= n; ++i) h[i] += dis[i], dis[i] = inf, vis[i] = false;
        }
        return ans;
    }
}
signed main(){
    cin >> _n >> m >> k;
    for(int i = 1; i <= _n; ++i) scanf("%d%d", &_r[i].x, &_r[i].y);
    for(int i = 1; i <= m; ++i) scanf("%d%d", &b[i].x, &b[i].y);
    sort(_r + 1, _r + 1 + _n, cmp);
    for(int i = 1; i <= _n; ++i) if(!n || r[n].y < _r[i].y) r[++n] = _r[i];
    reverse(r + 1, r + 1 + n);
    
    MCMF::n = 2, MCMF::S = 1, MCMF::T = 2;
    for(int i = 1; i <= n; ++i) idx[r[i].x] = ++MCMF::n, idy[r[i].y] = ++MCMF::n;
    for(int i = 1; i <= m; ++i){
        if(!idx.count(b[i].x)) idx[b[i].x] = ++MCMF::n;
        if(!idy.count(b[i].y)) idy[b[i].y] = ++MCMF::n;
    }
    int ld = -1, lx = -1;
    for(auto [d, x] : idy){
        if(lx != -1){
            MCMF::span(lx, x, 0, k);
            MCMF::span(x, lx, d - ld, k);
        }
        ld = d, lx = x;
    }
    MCMF::span(1, idy[r[1].y], 0, k);

    ld = -1, lx = -1;
    for(auto [d, x] : idx){
        if(lx != -1){
            MCMF::span(lx, x, d - ld, k);
            MCMF::span(x, lx, 0, k);
        }
        ld = d, lx = x;
    }
    MCMF::span(idx[r[n].x], 2, 0, k);

    for(int i = 1; i < n; ++i) MCMF::span(idx[r[i].x], idy[r[i + 1].y], 0, k);
    for(int i = 1; i <= m; ++i) MCMF::span(idy[b[i].y], idx[b[i].x], 0, 1);
    printf("%lld\n", MCMF::mcmf());
    return 0;
}
posted @ 2026-01-15 08:14  Zwi  阅读(4)  评论(0)    收藏  举报