# CF793G Oleg and chess(線段樹優化建圖)

CF793G Oleg and chess(線段樹優化建圖)

題目大意

有一個 \(n×n\) 的矩陣,每行每列至多能放一個棋子,另外有 \(q\) 個矩形的區域不能放棋子(這些矩形區域互不相交),問最多能放多少個棋子。\(n,q≤10^4\)

解題思路

做這題看不懂題解,想了簡單的做法,但 3400 就這嗎?結果寫了一下就 A 了(

首先你需要會幾個知識點即可:

  1. 行列連邊求最大匹配即爲答案。
  2. 會線段樹優化建圖
  3. 會掃描線

首先選一個格子可以看做這個格子所在的行和列匹配,最多選出格子的數量就是最大匹配,也是網絡流的最大流。

邊數太多考慮線段樹+掃描線優化建圖,我們維護行中沒有被矩形覆蓋的點,這個就是掃描線的模板題了,不知道爲什麼這題要保證矩形不交,看起來即使相交也可以做的。

具體說一下吧,離線下來,將所有的矩形拆成兩個邊界,從左向右掃,掃到矩形的左邊界則做線段覆蓋,那麼把線段樹對應的節點置爲 0 即可,掃到右邊界就是線段刪除,對應的節點新建一個點,向兩個兒子連邊即可。由於題目保證不相交,所以我們 build 的時候直接記錄區域內無限制時的節點編號即可。

總時間複雜度 \(Flows(n\log n,n \log n)\) 沒有其他題解裏所帶的 \(\Theta(n^2)\)

namespace Flows {
	const int M = 2005500;
	int cur[M], h[M], dep[M], ne[M], to[M], w[M], cnt, s, t, tot = 1;
	inline void add(int x, int y, int z) { ne[++tot] = h[x], to[h[x] = tot] = y, w[tot] = z; }
	inline void adde(int x, int y, int z) { add(x, y, z), add(y, x, 0); }
	int dfs(int x, int lim) {
		if (!lim || x == t) return lim;
		int res = 0, fl;
		for (int &i = cur[x], y; i; i = ne[i]) {
			if (dep[y = to[i]] != dep[x] + 1 || !w[i]) continue;
			fl = dfs(y, min(lim, w[i]));
			lim -= fl, res += fl;
			w[i] -= fl, w[i^1] += fl;
			if (!lim) return res;
		}
		return res;
	}
	queue<int> q;
	bool bfs(void) {
		memset(dep, 0, cnt * 4 + 20);
		dep[s] = 1, q.push(s);
		while (q.size()) {
			int x = q.front(); q.pop();
			for (int i = h[x], y; i; i = ne[i]) 
				if (!dep[y = to[i]] && w[i]) dep[y] = dep[x] + 1, q.push(y);
		}
		if (!dep[t]) return 0;
		memcpy(cur, h, cnt * 4 + 20);
		return 1;
	}
	int flow(void) {
		int ans = 0;
		while (bfs()) ans += dfs(s, 1e9);
		return ans;
	}
}
using Flows::cnt;
using Flows::adde;
using Flows::s;
using Flows::t;
using Flows::flow;

#define ls p << 1
#define rs ls | 1
const int hinf = 1e8;
const int N = 10500;
int id[N<<2], _1[N<<2], tag[N<<2], n, q;
void build(int p, int l, int r) {
	if (l == r) return adde(id[p] = _1[p] = l, t, 1);
	int mid = (l + r) >> 1; id[p] = _1[p] = ++cnt;
	build(ls, l, mid), build(rs, mid + 1, r);
	adde(_1[p], _1[ls], hinf), adde(_1[p], _1[rs], hinf);
}

void change(int p, int l, int r, int L, int R) {
	if (L <= l && r <= R) return tag[p] ^= 1, id[p] = tag[p] ? 0 : _1[p], void();
	int mid = (l + r) >> 1; id[p] = ++cnt;
	if (L <= mid) change(ls, l, mid, L, R);
	if (R > mid) change(rs, mid + 1, r, L, R);
	if (id[ls]) adde(id[p], id[ls], hinf);
	if (id[rs]) adde(id[p], id[rs], hinf);
}

vector<pair<int, int> > v1[N], v2[N];
int main() {
	read(n), read(q), s = cnt = n + 1, t = ++cnt;
	for (int i = 1, x1, x2, y1, y2;i <= q; ++i) {
		read(x1), read(y1), read(x2), read(y2);
		v1[x1].emplace_back(y1, y2), v2[x2 + 1].emplace_back(y1, y2);
	}
	build(1, 1, n);
	for (int i = 1;i <= n; ++i) {
		for (auto t: v2[i]) change(1, 1, n, t.fi, t.se);
		for (auto t: v1[i]) change(1, 1, n, t.fi, t.se);
		adde(s, id[1], 1);
	}
	write(flow());
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章