Codeforces 1615G. Maximum Adjacent Pairs (3300)
给定长度为 \(n\) 的序列 \(a\),\(a\) 中有些位置的值已经确定了,还有些位置的值没有确定,这些位置上的数是 \(0\),需要替换成 \(1\sim n\) 的值。
填完所有为 \(0\) 的位置使得满足下列条件的 \(k(k\in [1,n])\) 的数量最多,构造任意一种方案。
- \(\exists i\in [1,n)\),\(a_i=a_{i+1}=k\)。
\(2\le n\le 3\cdot 10^5,0\le a_i\le \min(n,600)\)。
有生之年在赛场上想出的题\((\)但没写完\()\)。
\(\color{green}{\text{observation:}}\)
考虑到有值的部分会将整个序列划分成若干段,只有与有值的位置相邻的不确定的值的位置才有可能与其匹配。
考虑一段长度为 \(len\) 且全为 \(0\) 的区间 \([l,r]\),分 \(len\) 奇偶讨论。
- 若 \(len\) 为奇数,此时必定可以对 \(len-1\) 个两两匹配,那么剩下一个要么跟 \(a_{l-1}\) 匹配,要么和 \(a_{r+1}\) 匹配。根据贪心策略,这样必定最优,假设 \(a_{l-1}\) 和 \(a_{r+1}\) 都匹配,那么最多达到的贡献为 \(2+\lfloor \frac{len-2}{2}\rfloor=\frac{len+1}{2}\),而前一种最多达到的贡献为 \(1+\lfloor \frac{len-1}{2}\rfloor=\frac{len+1}{2}\)。最多贡献一样,但后一种匹配两个可能会导致与其它的不能匹配,而且若后一种能匹配,必定前一种也能匹配,所以前一种的贡献始终 \(\ge\) 后一种的贡献。
- 若 \(len\) 为偶数,此时只有两种情况。一种是 \(a_{l-1}\) 和 \(a_{r+1}\) 都匹配,中间 \(len-2\) 个两两匹配。另一种是不管两边的,直接把中间 \(len\) 个两两匹配。根据贪心策略,这两种能涵盖所有的情况。前一种方案虽然最多贡献大,但是可能会对其它的匹配造成影响,所以需要两者都考虑。
然后可以自然地想到使用 一般图最大匹配 的带花树算法来解决。
考虑连边。
-
若 \(len\) 为奇数,将 \(a_{l-1}\) 连向一个新建的点 \(v\),\(a_{r+1}\) 也连向一个新建的点 \(v\)。那么 \(v\) 只能和其中之一匹配。
-
若 \(len\) 为偶数,将 \(a_{l-1}\) 连向一个新建的点 \(v_1\),\(a_{r+1}\) 连向一个新建的点 \(v_2\),再将 \(v_1\) 和 \(v_2\)。这恰好对应了讨论的两种情况,若 \(a_{l-1}\) 和 \(v_1\) 连,\(a_{r+1}\) 和 \(v_2\) 连,那么就是两边都匹配;否则 \(v_1\) 与 \(v_2\) 连,则是两边都不匹配。
时间复杂度 \(O(n+v^3)\),\(v\) 是 \(a_i\) 的值域。
#include <bits/stdc++.h>
#define eb emplace_back
using namespace std;
const int N = 6e5 + 5, M = 12e5 + 5;
int n, a[N], id = 1, have[N], head[N], tot, ver[M], nxt[M];
int fa[N], vis[N], pre[N], match[N], dfn[N], timer; bool del[M], vs[N];
queue <int> q; vector <int> clr; vector < pair<int, int> > vec;
inline void add(int u, int v) {
ver[++tot] = v; nxt[tot] = head[u]; head[u] = tot;
ver[++tot] = u; nxt[tot] = head[v]; head[v] = tot;
}
inline int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
inline int lca(int u, int v) {
timer ++, u = find(u), v = find(v);
while(dfn[u] != timer) {
dfn[u] = timer;
u = find(pre[match[u]]);
if(v) u ^= v ^= u ^= v;
}
return u;
}
inline void blossom(int x, int y, int z) {
while(find(x) != z) {
pre[x] = y; y = match[x];
if(vis[y] == 2) vis[y] = 1, q.push(y), clr.eb(y);
if(x == find(x)) fa[x] = z;
if(y == find(y)) fa[y] = z;
x = pre[y];
}
}
inline bool bfs(int s) {
for (auto x : clr) vis[x] = pre[x] = 0, fa[x] = x; clr.clear();
while(!q.empty()) q.pop();
q.push(s); vis[s] = 1; clr.eb(s);
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = head[u]; i; i = nxt[i]) {
int v = ver[i];
if(vis[v] == 2 || find(u) == find(v)) continue;
if(vis[v] == 0) {
vis[v] = 2; pre[v] = u; clr.eb(v);
if(!match[v]) {
for(int x = v, lst; x; x = lst) {
lst = match[pre[x]];
match[x] = pre[x];
match[pre[x]] = x;
}
return 1;
}
vis[match[v]] = 1;
q.push(match[v]);
clr.eb(match[v]);
}
else {
int w = lca(u, v);
blossom(u, v, w); blossom(v, u, w);
clr.eb(v), clr.eb(w);
}
}
}
return 0;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n + n; ++ i) fa[i] = i;
for (int i = 1; i <= n; ++ i) scanf("%d", a + i), have[a[i]] = 1;
for (int i = 1; i < n; ++ i) if (a[i] == a[i - 1] && a[i]) vs[a[i]] = 1;
for (int i = 1, j; i <= n; i = j + 1) {
j = i; if (a[i]) continue;
while (!a[j] && j <= n) ++ j;
vec.emplace_back(i, j);
if ((j - i) & 1) {
if (i > 1 && !vs[a[i - 1]]) add(a[i - 1], n + i);
if (j <= n && !vs[a[j]]) add(a[j], n + i);
} else {
if (i > 1 && !vs[a[i - 1]]) add(a[i - 1], n + i);
if (j <= n && !vs[a[j]]) add(a[j], n + j - 1);
add(n + i, n + j - 1);
}
}
for (int i = 1; i <= n + n; ++ i) if (!match[i]) bfs(i);
for (auto p : vec) {
int l = p.first, r = p.second;
if ((r - l) & 1) {
if (l > 1 && match[n + l] == a[l - 1]) {
a[l] = a[l - 1];
for (int i = l + 1; i + 1 < r; i += 2) {
while (have[id]) ++ id;
a[i] = a[i + 1] = id ++;
}
} else {
for (int i = l; i + 1 < r; i += 2) {
while (have[id]) ++ id;
a[i] = a[i + 1] = id ++;
}
a[r - 1] = a[r];
}
} else {
if (match[n + l] == n + r - 1) {
for (int i = l; i + 1 < r; i += 2) {
while (have[id]) ++ id;
a[i] = a[i + 1] = id ++;
}
} else {
a[l] = a[l - 1], a[r - 1] = a[r];
for (int i = l + 1; i + 1 < r; i += 2) {
while (have[id]) ++ id;
a[i] = a[i + 1] = id ++;
}
}
}
}
for (int i = 1; i <= n; ++ i) printf("%d ", a[i] ? a[i] : 1);
return 0;
}