[AGC056C] 01 Balanced 题解

[AGC056C] 01 Balanced 题解

有点巧妙这个题。

看到的第一反应是通过前缀和限制条件,限制区间内 0/1 的个数为一个定值且相邻两个数之间的差值 \(\le 1\)。那这个问题看上去可以通过差分约束来做,但由于有负权边要用 SPFA,时间复杂度会炸掉并且第二个条件也不好限制。

考虑换一种思路。我们记 \(x_i\) 表示区间 \(1\sim i\) 中 0 和 1 个数的差值。这样一来限制条件变成了 \(x_{l-1}=x_{r}\)\(|x_i-x_{i-1}|=1\)。首个条件用并查集缩起来即可,第二个条件我们拆成 \(x_i-x_{i-1}\le1\)\(x_{i-1}-x_i\le 1\) 用边权为 \(1\) 的差分 BFS 来维护。对于求字典序最小,实际上就是要 \(x\) 序列的字典序最大,那 BFS 过程中一定取到的是满足条件下的最大值,也实际上满足了字典序的限制。

那对于 \(x_i=x_{i-1}\) 的情况又该如何处理?实际上由于缩起来的所有点奇偶性一定相同,而相邻点之间边权为 \(1\),那这个东西实际上构成了一个二分图,不可能出现 \(x_i=x_{i-1}\) 的情形。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, m;
int fa[N];
int fnd(int x) {
	return x == fa[x] ? x : fa[x] = fnd(fa[x]);
}
void mge(int x, int y) {
	x = fnd(x), y = fnd(y);
	if (x ^ y) fa[x] = y;
}
vector<pair<int, int>>v[N];
int dis[N];

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	iota(fa + 1, fa + 1 + n, 1);
	for (int i = 0; i < m; i++) {
		int x, y;
		cin >> x >> y;
		--x;
		mge(x, y);
	}
	for (int i = 1; i <= n; i++) {
		v[fnd(i)].emplace_back(fnd(i - 1), 1);
		v[fnd(i - 1)].emplace_back(fnd(i), 1);
	}
	memset(dis, 0x3f, sizeof dis);
	dis[fnd(0)] = 0;
	queue<int>q;
	q.push(fnd(0));
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		for (auto p : v[x]) {
			int y = p.first, w = p.second;
			if (dis[y] > 1e9) dis[y] = dis[x] + w, q.push(y);
		}
	}
	for (int i = 0; i < n; i++) {
		if (dis[fnd(i)] < dis[fnd(i + 1)]) cout << "0";
		else cout << "1";
	}
	cout << '\n';
	return 0;
}
posted @ 2025-07-01 21:45  长安19路  阅读(6)  评论(0)    收藏  举报