Codeforces 1615G. Maximum Adjacent Pairs (3300)

给定长度为 n 的序列 aa 中有些位置的值已经确定了,还有些位置的值没有确定,这些位置上的数是 0,需要替换成 1n 的值。
填完所有为 0 的位置使得满足下列条件的 k(k[1,n]) 的数量最多,构造任意一种方案。

  • i[1,n)ai=ai+1=k

2n3105,0aimin(n,600)


有生之年在赛场上想出的题(但没写完)

observation:
考虑到有值的部分会将整个序列划分成若干段,只有与有值的位置相邻的不确定的值的位置才有可能与其匹配。
考虑一段长度为 len 且全为 0 的区间 [l,r],分 len 奇偶讨论。

  • len 为奇数,此时必定可以对 len1 个两两匹配,那么剩下一个要么跟 al1 匹配,要么和 ar+1 匹配。根据贪心策略,这样必定最优,假设 al1ar+1 都匹配,那么最多达到的贡献为 2+len22=len+12,而前一种最多达到的贡献为 1+len12=len+12。最多贡献一样,但后一种匹配两个可能会导致与其它的不能匹配,而且若后一种能匹配,必定前一种也能匹配,所以前一种的贡献始终 后一种的贡献。
  • len 为偶数,此时只有两种情况。一种是 al1ar+1 都匹配,中间 len2 个两两匹配。另一种是不管两边的,直接把中间 len 个两两匹配。根据贪心策略,这两种能涵盖所有的情况。前一种方案虽然最多贡献大,但是可能会对其它的匹配造成影响,所以需要两者都考虑。

然后可以自然地想到使用 一般图最大匹配 的带花树算法来解决。

考虑连边。

  • len 为奇数,将 al1 连向一个新建的点 var+1 也连向一个新建的点 v。那么 v 只能和其中之一匹配。

  • len 为偶数,将 al1 连向一个新建的点 v1ar+1 连向一个新建的点 v2,再将 v1v2。这恰好对应了讨论的两种情况,若 al1v1 连,ar+1v2 连,那么就是两边都匹配;否则 v1v2 连,则是两边都不匹配。

时间复杂度 O(n+v3)vai 的值域。

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 @   Samsara-soul  阅读(562)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示