[University CodeSprint 4] Drawing Rectangles (扫描线 + 最小点覆盖)
[University CodeSprint 4] Drawing Rectangles
扫描线 + 最小点覆盖
题目的形式一看就是扫描线,观察到矩形的并面积 \(\le3\times10^5\),所以可以直接把这些位置找出来。这部分的复杂度是 \(O(n\log n)\)。
然后剩下的部分就是一个经典的最小点覆盖问题。具体的说,构造二分图,左边代表行,右边代表列,对于位置 \((i,j)\),连边 \((i,j)\),求最少选点使得所有边至少有一个点被选中。跑网络流求最小点覆盖即可。这部分复杂度 \(O(m\sqrt{n})\)。
对于输出方案,考虑遍历求完最大匹配的网络流,这样构造:
- 从每个左部非匹配点出发,一次走非匹配边,匹配边,非匹配边......并标记经过节点。(增广路)
- 左部点选未标记节点,右部点选标记节点,这样构成的集合即为最小点覆盖。
这个构造为 König 定理证明最大匹配等于最小点覆盖的部分。
总复杂度 \(\Theta(n\log n+m\sqrt{n})\)。线段树写错了,多调了 2h。(恼
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back
using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 3e5 + 10;
int n, cnt = 1, mxy, m, k, s, t;
struct node {
int to, nxt, w;
} e[N << 3];
int h[N << 3], mark[N << 3];
void add(int u, int v, int w) {
e[++cnt].to = v;
e[cnt].nxt = h[u];
e[cnt].w = w;
h[u] = cnt;
}
int tot;
struct line {
int y, x1, x2, v;
} l[N << 1];
bool cmp(line a, line b) {
return a.y < b.y;
}
struct seg {
int cnt, lzy;
} T[N << 3];
void pushup(int u) {
T[u].cnt = T[u << 1].cnt + T[u << 1 | 1].cnt;
}
void mdf(int u, int l, int r, int x) {
T[u].cnt += (r - l + 1) * x;
T[u].lzy += x;
}
void pd(int u, int l, int r) {
if(!T[u].lzy) return;
int mid = (l + r) >> 1;
mdf(u << 1, l, mid, T[u].lzy);
mdf(u << 1 | 1, mid + 1, r, T[u].lzy);
T[u].lzy = 0;
}
void upd(int u, int l, int r, int L, int R, int x) {
if(L <= l && r <= R) {
mdf(u, l, r, x);
return;
}
int mid = (l + r) >> 1; pd(u, l, r);
if(L <= mid) upd(u << 1, l, mid, L, R, x);
if(R > mid) upd(u << 1 | 1, mid + 1, r, L, R, x);
pushup(u);
}
void qry(int u, int l, int r) {
if(l == r) {
mark[l] = mark[k + m] = 1;
add(l, k + m, 1), add(k + m, l, 0);
return;
}
int mid = (l + r) >> 1; pd(u, l, r);
if(T[u << 1].cnt) qry(u << 1, l, mid);
if(T[u << 1 | 1].cnt) qry(u << 1 | 1, mid + 1, r);
}
int dis[N << 3], now[N << 3], used[N << 3];
bool bfs() {
std::queue<int> q;
for (int i = 1; i <= t; i++) dis[i] = iinf;
dis[s] = 0;
q.push(s);
now[s] = h[s];
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = h[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(e[i].w > 0 && dis[v] == iinf) {
dis[v] = dis[u] + 1;
q.push(v);
now[v] = h[v];
if(v == t) return 1;
}
}
}
return 0;
}
int dfs(int u, int sum) {
if(u == t) return sum;
int minn, ans = 0;
for(int i = now[u]; i && sum; i = e[i].nxt) {
now[u] = i;
int v = e[i].to;
if(e[i].w && dis[v] == dis[u] + 1) {
minn = dfs(v, std::min(e[i].w, sum));
if(!minn) dis[v] = iinf;
e[i].w -= minn;
e[i ^ 1].w += minn;
ans += minn;
sum -= minn;
}
}
return ans;
}
int dinic() {
int ans = 0;
while(bfs()) {
ans += dfs(s, iinf);
}
return ans;
}
void path(int u) {
used[u] = 1;
for(int i = h[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(!used[v] && e[i].w) path(v);
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n;
for (int i = 1; i <= n; i++) {
int a, b, c, d;
std::cin >> a >> b >> c >> d;
a++, b++, c++, d++;
l[++tot] = {b, a, c, 1}, l[++tot] = {d + 1, a, c, -1};
mxy = std::max(mxy, d);
m = std::max(m, c);
}
std::sort(l + 1, l + tot + 1, cmp);
int nw = 1;
for (int i = 1; i <= mxy; i++) {
k = i;
while(nw <= tot && l[nw].y == i) {
upd(1, 1, m, l[nw].x1, l[nw].x2, l[nw].v);
nw++;
}
qry(1, 1, m);
}
s = m + mxy + 1, t = s + 1;
for (int i = 1; i <= m; i++) {
if(mark[i]) add(s, i, 1), add(i, s, 0);
}
for (int i = 1; i <= mxy; i++) {
if(mark[i + m]) add(i + m, t, 1), add(t, i + m, 0);
}
std::cout << dinic() << "\n";
path(s);
std::vector<int> col, row;
for (int i = h[s]; i; i = e[i].nxt) {
int v = e[i].to;
if(!used[v]) col.pb(v);
}
for (int i = h[t]; i; i = e[i].nxt) {
int v = e[i].to;
if(used[v]) row.pb(v);
}
std::sort(row.begin(), row.end());
std::sort(col.begin(), col.end());
std::cout << col.size() << "\n";
for (auto x : col) std::cout << x - 1 << " ";
std::cout << "\n";
std::cout << row.size() << "\n";
for (auto x : row) std::cout << x - m - 1 << " ";
std::cout << "\n";
return 0;
}