题解 [ARC122F] Domination
一个破题做一下午加一晚上跟颓废有什么区别
首先发现只有单调栈中的红色石头是有用的
然后发现操作等价于移动蓝色石头覆盖其左下的红色石头
发现每个红色石头都要被覆盖 \(\geqslant k\) 次
然后发现覆盖之间是独立的
那么可以拆分成 \(k\) 个 \(k=1\) 的方案
考虑 \(k=1\) 怎么做
发现现在是区间覆盖,可以转化成路径问题
用石头 \(i\) 覆盖 \([l, r]\) 等价于一条 \(l\to i\to r\) 的路径
这样边数很大
一个 trick 是利用坐标轴
将贡献拆成 \(\max(y_l-y_i, 0)+\max(x_r-x_i, 0)\)
那么在 \(x\) 轴正方向走一步代价为 1,反方向走一步代价为 0
\(y\) 轴向下走一步代价为 1,向上走一步代价为 0
一个蓝色石头可以无代价从 \(y_i\) 跳到 \(x_i\)
为了让路径经过坐标轴,从红色石头向 \(y_i\) 连边,从 \(x_i\) 向下一个红色石头连边
于是可以用最短路求 \(k=1\) 的答案了
然后 \(k>1\),发现每个蓝色石头只能用一次,就变成了费用流
然而 EK 是可以卡的,需要 zkw 费用流
- 原始边权均非负的费用流图是没有负环的
从 rvalue 学姐博里粘个证明:Q: 你这个费用流里既然会冒出负权来, 那要是推反向边的时候在残量网络里増广出负环了怎么破?
A: EK费用流的过程每次只増广最短路, 所以任意时刻増广出来的流一定都是最小费用流. 但是如果残量网络中存在负环, 那么我们显然可以让一部分流量改道流经这个负环来让费用减少, 这样就矛盾了. 所以一定不会増广出负环. - 注意 zkw/原始对偶 若使用 \(\tt spfa\) 预处理 \(h\) 的话复杂度仍是 \(O(nmf)\) 的
所以若图中没有负环就不要跑这个预处理了
于是复杂度 \(O(kn\log n)\)(\(n, m\) 同阶)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 3000010
#define fir first
#define sec second
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m, k;
#undef unix
bool vis[N];
ll dis[N], h[N], ans;
int head[N], back[N], inc[N], ecnt=1;
pair<int, int> sta[N], red[N], blue[N];
int unix[N], uniy[N], id[N], idx[N], idy[N], tot, s, t, xsiz, ysiz, top;
struct edge{int to, next, flw; ll cst;}e[N<<1];
inline void add(int s, int t, int f, ll c) {/* cout<<s<<' '<<t<<' '<<f<<' '<<c<<endl; */ e[++ecnt]={t, head[s], f, c}; head[s]=ecnt;}
void spfa(int s, int t) {
memset(h, 0x3f, sizeof(h));
queue<int> q;
h[s]=0; q.push(s);
while (q.size()) {
int u=q.front(); q.pop();
vis[u]=0;
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (e[i].flw && h[u]+e[i].cst<h[v]) {
h[v]=h[u]+e[i].cst;
if (!vis[v]) q.push(v), vis[v]=1;
}
}
}
}
bool dijkstra(int s, int t) {
for (int i=1; i<=tot; ++i) dis[i]=0x3f3f3f3f3f3f3f3f, back[i]=-1, vis[i]=0;
priority_queue<pair<ll, int>> q;
q.push({dis[s]=0, s}); inc[s]=INF;
while (q.size()) {
int u=q.top().sec; q.pop();
if (vis[u]) continue;
for (int i=head[u],v; ~i; i=e[i].next) {
v=e[i].to; ll val=e[i].cst+h[u]-h[v];
if (e[i].flw && dis[u]+val<dis[v]) {
dis[v]=dis[u]+val;
back[v]=i; inc[v]=min(inc[u], e[i].flw);
q.push({-dis[v], v});
}
}
}
return ~back[t];
}
// bool spfa(int s, int t) {
// memset(dis, 127, sizeof(dis));
// memset(back, -1, sizeof(back));
// dis[s]=0; inc[s]=INF;
// queue<int> q;
// q.push(s);
// int u;
// while (q.size()) {
// u=q.front(); q.pop();
// vis[u]=0;
// for (int i=head[u],v; ~i; i=e[i].next) {
// v = e[i].to;
// if (e[i].flw && dis[v]>dis[u]+e[i].cst) {
// dis[v]=dis[u]+e[i].cst;
// back[v]=i; inc[v]=min(inc[u], e[i].flw);
// if (!vis[v]) q.push(v), vis[v]=1;
// }
// }
// }
// return ~back[t];
// }
signed main()
{
n=read(); m=read(); k=read();
memset(head, -1, sizeof(head));
for (int i=1; i<=n; ++i) red[i].fir=read(), red[i].sec=read();
for (int i=1; i<=m; ++i) blue[i].fir=read(), blue[i].sec=read();
sort(red+1, red+n+1);
for (int i=1; i<=n; ++i) {
while (top && sta[top].sec<=red[i].sec) --top;
sta[++top]=red[i];
}
for (int i=1; i<=top; ++i) unix[++xsiz]=sta[i].fir, uniy[++ysiz]=sta[i].sec;
for (int i=1; i<=m; ++i) unix[++xsiz]=blue[i].fir, uniy[++ysiz]=blue[i].sec;
sort(unix+1, unix+xsiz+1);
sort(uniy+1, uniy+ysiz+1);
xsiz=unique(unix+1, unix+xsiz+1)-unix-1;
ysiz=unique(uniy+1, uniy+ysiz+1)-uniy-1;
for (int i=1; i<=top; ++i) sta[i]={lower_bound(unix+1, unix+xsiz+1, sta[i].fir)-unix, lower_bound(uniy+1, uniy+ysiz+1, sta[i].sec)-uniy};
for (int i=1; i<=m; ++i) blue[i]={lower_bound(unix+1, unix+xsiz+1, blue[i].fir)-unix, lower_bound(uniy+1, uniy+ysiz+1, blue[i].sec)-uniy};
// cout<<"siz: "<<xsiz<<' '<<ysiz<<endl;
// cout<<"sta: "; for (int i=1; i<=top; ++i) cout<<"("<<sta[i].fir<<','<<sta[i].sec<<") "; cout<<endl;
// cout<<"blue: "; for (int i=1; i<=m; ++i) cout<<"("<<blue[i].fir<<','<<blue[i].sec<<") "; cout<<endl;
for (int i=1; i<=xsiz; ++i) idx[i]=++tot;
for (int i=1; i<=ysiz; ++i) idy[i]=++tot;
for (int i=1; i<=top+1; ++i) id[i]=++tot;
for (int i=1; i<xsiz; ++i) add(idx[i], idx[i+1], INF, unix[i+1]-unix[i]), add(idx[i+1], idx[i], 0, unix[i]-unix[i+1]); //, cout<<unix[i+1]-unix[i]<<endl;
for (int i=2; i<=xsiz; ++i) add(idx[i], idx[i-1], INF, 0), add(idx[i-1], idx[i], 0, 0);
for (int i=2; i<=ysiz; ++i) add(idy[i], idy[i-1], INF, uniy[i]-uniy[i-1]), add(idy[i-1], idy[i], 0, uniy[i-1]-uniy[i]); //, cout<<uniy[i]-uniy[i-1]<<endl;
for (int i=1; i<ysiz; ++i) add(idy[i], idy[i+1], INF, 0), add(idy[i+1], idy[i], 0, 0);
for (int i=1; i<=top; ++i) {
add(id[i], idy[sta[i].sec], k, 0), add(idy[sta[i].sec], id[i], 0, 0);
add(idx[sta[i].fir], id[i+1], k, 0), add(id[i+1], idx[sta[i].fir], 0, 0);
}
for (int i=1; i<=m; ++i) add(idy[blue[i].sec], idx[blue[i].fir], 1, 0), add(idx[blue[i].fir], idy[blue[i].sec], 0, 0);
s=id[1]; t=id[top+1];
// spfa(s, t);
int tem=0;
while (dijkstra(s, t)) {
for (int i=1; i<=tot; ++i) h[i]+=dis[i];
tem+=inc[t];
ans+=h[t]*inc[t];
for (int u=t; u!=s; u=e[back[u]^1].to) {
e[back[u]].flw-=inc[t];
e[back[u]^1].flw+=inc[t];
}
}
// while (spfa(s, t)) {
// tem+=inc[t];
// ans+=dis[t]*inc[t];
// for (int u=t; u!=s; u=e[back[u]^1].to) {
// e[back[u]].flw-=inc[t];
// e[back[u]^1].flw+=inc[t];
// }
// }
// assert(tem==k);
printf("%lld\n", ans);
// cout<<tot<<' '<<ecnt-1<<' '<<s<<' '<<t<<endl;
return 0;
}