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\) 的值域。

\(\color{blue}{\text{code}}\)

#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;
}
posted @ 2021-12-26 10:34  Samsara-soul  阅读(554)  评论(0编辑  收藏  举报