CF793G Oleg and chess(線段樹優化建圖)
題目大意
有一個 \(n×n\) 的矩陣,每行每列至多能放一個棋子,另外有 \(q\) 個矩形的區域不能放棋子(這些矩形區域互不相交),問最多能放多少個棋子。\(n,q≤10^4\)
解題思路
做這題看不懂題解,想了簡單的做法,但 3400 就這嗎?結果寫了一下就 A 了(
首先你需要會幾個知識點即可:
- 行列連邊求最大匹配即爲答案。
- 會線段樹優化建圖
- 會掃描線
首先選一個格子可以看做這個格子所在的行和列匹配,最多選出格子的數量就是最大匹配,也是網絡流的最大流。
邊數太多考慮線段樹+掃描線優化建圖,我們維護行中沒有被矩形覆蓋的點,這個就是掃描線的模板題了,不知道爲什麼這題要保證矩形不交,看起來即使相交也可以做的。
具體說一下吧,離線下來,將所有的矩形拆成兩個邊界,從左向右掃,掃到矩形的左邊界則做線段覆蓋,那麼把線段樹對應的節點置爲 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;
}