题解:AT_agc076_b [AGC076B] Split and Reverse

感觉细节有点多的题啊,基本跟官方题解讲的差不多,感觉不是很好想。

题意:给出一个 01 序列 \(a\),你每次可以进行这样一个操作:

  • 把所有的位置分配到 \(A,B\) 两个集合中,然后把这两个集合中的元素 reverse 再塞回去。

问将其排序最少需要多少次操作并给出构造。\(\sum n\le 2\times 10^5\)

做法:

观察到我需要给构造,所以猜测答案不会很大。

\(0\) 次操作判定显然,\(1\) 次操作貌似也能简单构造,\(\ge 2\) 次操作的好像很难判定,我们就猜他是两次可以构造完。

考虑一次操作的需要满足什么条件,记 \(C\) 为序列中 \(0\) 的个数。那么对于 \([1,C]\) 中的 \(1\),我们需要全部扔到右边去,对于 \([C+1,n]\) 中的 \(0\),我们需要全部扔到左边去。对于本身就在原来位置的 \(0,1\),我们需要他作为一个对称的中心。那么记最左侧的 \(0\) 位置为 \(L\),最右侧的 \(1\) 位置为 \(R\),我们不妨假设 \(L\) 分配到集合 \(A\),那么我们要求 \([L,C]\) 中的 \(0\)\(A\) 集合中作为对称部分,\([L,C]\) 中的 \(1\)\(B\) 集合中,记有 \(x\) 个,但是是作为两侧需要对称过去的东西。

那么我们注意到,\([R+1,n]\) 这一部分全都是 \(0\),我们就需要把这 \(x\)\(1\) 都跟他们交换,所以限制是 \(\max(n - R,n-C)\ge x\)。满足限制,我们就把 \([\max(n - R,n-C)+1,\max(n - R,n-C)+x]\) 这部分扔到集合 \(A\) 中作为这些 \(1\) 对称的位置。对 \([C+1,R]\) 这部分讨论也可以得到一个限制和放置方式。然后对于剩下的,我们计算后会发现是开头为 \(1\),末尾为 \(0\),且两者个数一样,直接全部丢到同一个集合即可。

那么考虑怎么做两次操作的,我们考虑去凑一次操作的形式。为了方便,我们这里假设 \(C\ge n - C\)

我们考虑,如果最后 \(n-C\) 个位置满足形如 \(111\cdots 000\) 的形式,那么一定是可以一次操作得到的,因为我们最多需要放等于 \(0\) 个数的 \(1\) 过来,满足判定条件。

所以考虑对称一下,找前 \(n-C\) 个数中有 \(y\)\(0\) 并和最后 \(y\) 个数分为一组,其他的数分为一组,那么前 \(n-C\) 个数中的 \(1\) 就会全部扔到最后 \(n-C\) 个数的前缀,最后 \(y\) 个数都是 \(0\),就可以一次操作解决了。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2.5e5 + 5, mod = 998244353;
int n, a[maxn], b[maxn];
bool chk0(int a[]) {
	for (int i = 1; i < n; i++)
		if(a[i] > a[i + 1])
			return 0;
	return 1;
}
string s;
vector<int> pos[2], v[2];
void change(string s, int *a) {
	s = ' ' + s;
	pos[0].clear(), pos[1].clear(), v[0].clear(), v[1].clear();
	for (int i = 1; i <= n; i++)
		pos[s[i] - 'A'].push_back(i),
		v[s[i] - 'A'].push_back(a[i]);
	reverse(v[0].begin(), v[0].end());
	reverse(v[1].begin(), v[1].end());
	for (int i = 0; i < v[0].size(); i++)
		a[pos[0][i]] = v[0][i];
	for (int i = 0; i < v[1].size(); i++)
		a[pos[1][i]] = v[1][i];
}
bool chk1(int a[], int ct) {
	int lx = 0, rx = 0;
	s = " ";
	for (int i = 1; i <= n; i++)
		s += ' ';
	for (int i = 1; i <= n; i++) {
		if(!a[i] && !lx)
			lx = i;
		if(a[i])
			rx = i;
	}
	int lh = 0, rh = 0;
	for (int i = lx; i <= ct; i++) {
		lh += a[i];
		if(a[i])
			s[i] = 'A';
		else
			s[i] = 'B';
	}
	for (int i = ct + 1; i <= rx; i++) {
		rh += !a[i];
		if(!a[i])
			s[i] = 'B';
		else	
			s[i] = 'A';
	}
//	cout << lh << " " << rh << " " << lx << " " << rx << " " << s[3] << " " << s[2] << endl;
//	if(lx - 1 > ct) {
//		exit(12313);
//	}
	if(lh <= min(n - ct, n - rx) && rh <= min(ct, lx - 1)) {
		for (int i = max(ct + 1, rx + 1); i <= max(ct + 1, rx + 1) + lh - 1; i++)
			s[i] = 'A';
		for (int i = min(ct, lx - 1); i >= min(ct, lx - 1) - rh + 1; i--)
			s[i] = 'B';
		for (int i = 1; i <= min(ct, lx - 1) - rh; i++)
			s[i] = 'A';
		for (int i = max(ct + 1, rx + 1) + lh; i <= n; i++)
			s[i] = 'A';
		return 1;
	}
	return 0;
}
void solve() {
	cin >> n;
	int cnt = 0;
	for (int i = 1; i <= n; i++)
		cin >> a[i], b[i] = a[i], cnt += b[i] == 0;
	if(chk0(a)) {
		cout << 0 << endl;
		return ;
	}
	if(chk1(b, cnt)) {
		s.erase(0, 1);
		cout << 1 << endl << s << endl;
		change(s, a);
		if(!chk0(a))
			exit(1);
		return ;
	}
	cout << 2 << endl;
	if(cnt >= n - cnt) {
		int ind = 0;
		s = " ";
		for (int i = 1; i <= n; i++)
			s += ' ';
		for (int i = 1; i <= n - cnt; i++) {
			if(!a[i])
				ind++, s[i] = 'A';
			else
				s[i] = 'B';
		}
		for (int i=  n - cnt + 1; i <= cnt; i++)
			s[i] = 'B';
		for (int i = n; i >= n - ind + 1; i--)
			s[i] = 'A';
		for (int i = cnt + 1; i <= n - ind; i++)
			s[i] = 'B';
		s.erase(0, 1);
		cout << s << endl;
		change(s, a);
		chk1(a, cnt);
		s.erase(0, 1);
		cout << s << endl;
		change(s, a);
	//	if(!chk0(a))
	//		exit(1);
	}
	else {
		int ind = 0;
		s = " ";
		for (int i = 1; i <= n; i++)
			s += ' ';
		for (int i = n - cnt + 1; i <= n; i++) {
			if(a[i])
				ind++, s[i] = 'A';
			else
				s[i] = 'B';
		}
		for (int i = cnt + 1; i <= n - cnt; i++)
			s[i] = 'B';
		for (int i = 1; i <= ind; i++)
			s[i] = 'A';
		for (int i = ind + 1; i <= cnt; i++)
			s[i] = 'B';
		s.erase(0, 1);
		cout << s << endl;
		change(s, a);
		chk1(a, cnt);
		s.erase(0, 1);
		cout << s << endl;
		change(s, a);
	//	if(!chk0(a))
	//		exit(1);
	}
}
signed main() {
//	freopen("test.in", "r", stdin);
//	freopen("std.out", "w", stdout);
	int T; cin >> T;
	while(T--)
		solve();
	return 0;
}
posted @ 2026-01-05 22:28  LUlululu1616  阅读(8)  评论(4)    收藏  举报