Loading

[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})\)

对于输出方案,考虑遍历求完最大匹配的网络流,这样构造:

  1. 从每个左部非匹配点出发,一次走非匹配边,匹配边,非匹配边......并标记经过节点。(增广路)
  2. 左部点选未标记节点,右部点选标记节点,这样构成的集合即为最小点覆盖。

这个构造为 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;
}
posted @ 2024-07-04 17:15  Fire_Raku  阅读(1)  评论(0编辑  收藏  举报