题解: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;
}

浙公网安备 33010602011771号