题解:P10683 [COTS 2024] 划分 Particija

· · 题解

分析

对于每个 ia_ib_i + n 连边,表示集合 a_i 和集合 b_i+n 至少有一个要选。连出来是若干个联通二分图。

对于一个联通二分图,要么左部点全选,要么右部点全选。时间复杂度 $O(n)$。 $k=1$。 等价于修改一条边 $(u,v)$ 的某个端点。不妨设 $u$ 是左部点,修改 $v$。修改后有几种情况: 1. 修改后这个联通二分图不变。答案不变。 2. 修改后两个联通二分图合并。答案增加。因为 $\min(a,b)+\min(c,d)\le \min(a+c,b+d)$。 3. 修改后这个联通二分图分裂。答案减小。理由同 $2$。 因为最小化代价,所以选择情况 $3$ 不劣。则选择的边为这个联通二分图的割边。把左部点看成黑点,右部点看成白点(染色)(可以直接钦定黑点为 $a$ 中集合的点,白点为 $b$ 中集合的点)。维护每条割边两边黑白点数量即可。时间复杂度 $O(n)$。 $k=2$。 选择情况 $2$ 不劣。分情况讨论: 1. 选择的边不是割边。记黑点数量为 $a$,白点数量为 $b$。另一个连通块黑点数量为 $c$,白点为 $d$。 1. $a+c \le b+d$。等价于 $d-c \ge a-b$。那么就是求 $d-c \ge a-b$ 中,$c -\min(c,d)$ 的最大值(因为增加量为 $-\min(a,b) + (a+c) -\min(c,d)$)。注意要求这一对 $(c,d)$ 不是当前连通块。 2. $b+d \le a+c$。等价于 $c-d \ge b-a$。那么就是求 $c-d \ge b-a$ 中,$d -\min(c,d)$ 的最大值。 2. 选择的这条边是割边。枚举分成两个连通块后是哪一个和其它某个连通块合并。然后就和情况 $1$ 一样了。 时间复杂度 $O(n\log n)$。 ## 代码 ```cpp const int N = 4e5 + 10, inf = 1e9 + 7; int n, a[N], b[N]; int dfn[N], low[N], idx; pii E[N]; int m; bool st[N]; struct node{ pii x, y; int col; } spt[N]; vector<pii> e[N]; int n1, n2; int col[N], c; pii cnt[N]; struct Node{ int dt, val, c, typ; } q1[N << 1], q2[N << 1]; int l1, l2; int dtc[N], Len; il pii dfs(int u, int fr){ pii Cnt = {0, 0}; if(u <= n1) ++ Cnt.x; else ++ Cnt.y; col[u] = c; dfn[u] = low[u] = ++ idx; for(auto [v, id]: e[u]) if(id != fr){ spt[id].col = c; if(! dfn[v]){ pii Cnt_ = dfs(v, id); low[u] = min(low[u], low[v]); if(low[v] > dfn[u]){ st[id] = 1; spt[id].y = Cnt_; spt[id].col = c; } Cnt.x += Cnt_.x; Cnt.y += Cnt_.y; } else{ low[u] = min(low[u], dfn[v]); } } return Cnt; } il void init(bool fc = 0){ n = rd, m = 0; n1 = 0, n2 = 0; Len = 0; U(i, 1, n) a[i] = rd, dtc[++ Len] = a[i]; sort(dtc + 1, dtc + Len + 1), Len = unique(dtc + 1, dtc + Len + 1) - (dtc + 1); U(i, 1, n) a[i] = lower_bound(dtc + 1, dtc + Len + 1, a[i]) - dtc; n1 = Len; Len = 0; U(i, 1, n) b[i] = rd, dtc[++ Len] = b[i]; sort(dtc + 1, dtc + Len + 1), Len = unique(dtc + 1, dtc + Len + 1) - (dtc + 1); U(i, 1, n) b[i] = lower_bound(dtc + 1, dtc + Len + 1, b[i]) - dtc; n2 = Len; U(i, 1, n * 2) dfn[i] = low[i] = st[i] = 0, e[i].clear(); idx = 0, c = 0; U(i, 1, n){ E[++ m] = {a[i], b[i] + n1}; // cout << a[i]<<" "<<b[i]+n1<<"\n"; e[a[i]].push_back({b[i] + n1, m}); e[b[i] + n1].push_back({a[i], m}); } if(fc == 1){ cout << n << " " << m << "\n"; U(i, 1, n) cout<< a[i] << " "; puts(""); U(i, 1, n) cout<< b[i] << " "; puts("\n"); } U(i, 1, n1 + n2) if(! dfn[i]){ ++ c; cnt[c] = dfs(i, 0); } U(i, 1, m) if(st[i]){ int u = spt[i].col; spt[i].x = {cnt[u].x - spt[i].y.x, cnt[u].y - spt[i].y.y}; } return ; } il void solve0(){ int res = 0; U(i, 1, c) res += min(cnt[i].x, cnt[i].y); cout << res << "\n"; return ; } il void solve1(){ int res = 0, sum = 0; U(i, 1, c) res += min(cnt[i].x, cnt[i].y); sum = res; U(i, 1, m) if(st[i]){ int u = spt[i].col; int res_ = sum - min(cnt[u].x, cnt[u].y); res_ += min(spt[i].x.x, spt[i].x.y) + min(spt[i].y.x, spt[i].y.y); res = min(res, res_); } cout << max(1, res) << "\n"; return ; } il void solve2(){ l1 = l2 = 0; int res = 0, sum = 0; U(i, 1, c) res += min(cnt[i].x, cnt[i].y); sum = res; U(i, 1, c){ int C = cnt[i].x, D = cnt[i].y; int delt = D - C; int val = C - min(C, D); q1[++ l1] = {delt, val, i, 0}; } if(n1 < n){ int C = 1, D = 0; int delt = D - C; int val = C - min(C, D); q1[++ l1] = {delt, val, c + 1, 0}; } if(n2 < n){ int C = 0, D = 1; int delt = D - C; int val = C - min(C, D); q1[++ l1] = {delt, val, c + 1, 0}; } U(i, 1, c){ int C = cnt[i].x, D = cnt[i].y; int delt = C - D; int val = D - min(C, D); q2[++ l2] = {delt, val, i, 0}; } if(n1 < n){ int C = 1, D = 0; int delt = C - D; int val = D - min(C, D); q2[++ l2] = {delt, val, c + 1, 0}; } if(n2 < n){ int C = 0, D = 1; int delt = C - D; int val = D - min(C, D); q2[++ l2] = {delt, val, c + 1, 0}; } U(i, 1, m){ int u = spt[i].col; int res_ = res - min(cnt[u].x, cnt[u].y); if(st[i]){ { pii x = spt[i].x; int A = x.x, B = x.y; int delt = A - B; q1[++ l1] = {delt, res_ + A + min(spt[i].y.x, spt[i].y.y), u, 1}; delt = B - A; q2[++ l2] = {delt, res_ + B + min(spt[i].y.x, spt[i].y.y), u, 1}; } { pii x = spt[i].y; int A = x.x, B = x.y; int delt = A - B; q1[++ l1] = {delt, res_ + A + min(spt[i].x.x, spt[i].x.y), u, 1}; delt = B - A; q2[++ l2] = {delt, res_ + B + min(spt[i].x.x, spt[i].x.y), u, 1}; } } else{ { pii x = cnt[u]; int A = x.x, B = x.y; int delt = A - B; q1[++ l1] = {delt, res_ + A, u, 1}; delt = B - A; q2[++ l2] = {delt, res_ + B, u, 1}; } } } sort(q1 + 1, q1 + l1 + 1, [](Node x, Node y){ if(x.dt != y.dt) return x.dt > y.dt; return x.typ < y.typ; }); priority_queue<pii> Q1; U(i, 1, l1){ if(q1[i].typ == 0) Q1.push({q1[i].val, q1[i].c}); else{ if(Q1.empty()) continue; if(Q1.top().y != q1[i].c) res = max(res, Q1.top().x + q1[i].val); else{ pii x = Q1.top(); Q1.pop(); if(!Q1.empty()) res = max(res, Q1.top().x + q1[i].val); Q1.push(x); } } } sort(q2 + 1, q2 + l2 + 1, [](Node x, Node y){ if(x.dt != y.dt) return x.dt > y.dt; return x.typ < y.typ; }); priority_queue<pii> Q2; U(i, 1, l2){ if(q2[i].typ == 0) Q2.push({q2[i].val, q2[i].c}); else{ if(Q2.empty()) continue; if(Q2.top().y != q2[i].c) res = max(res, Q2.top().x + q2[i].val); else{ pii x = Q2.top(); Q2.pop(); if(!Q2.empty()) res = max(res, Q2.top().x + q2[i].val); Q2.push(x); } } } cout << res << "\n"; return ; } ```