codeforces 题目 Complementary XOR

目录

题目:

题目理解:

初始思路:

初始思路AC代码:

更优化的思路:

更优化的思路的AC代码


题目:

题目理解:

给你长度 n ,再分别给你长度为 n 的 a 字符串和 b 字符串,这两串都是 01字符串。

一次操作可以进行如下操作:

①找出 1 到 n 的一段连续区间(最小长度为 1 )

②将 a 串下标位于区间内(闭区间)的元素进行反转( 1 变 0 , 0 变 1 )

③将 b 串下标位于区间的元素进行反转( 1 变 0 , 0 变 1 )

目标:找出一种操作次数小于( n + 5 )的操作方法,使得两个字符串里面的元素最终都变成 0

初始思路:

先进行尝试,发现有几个特性:

①区间选为 1 到 n 的话,可以将 a 串完全翻转而同时 b 保持不变

②通过题目给的样例和自己尝试发现似乎只有 a 串和 b 串完全相同过着完全相反才能成功

③ a 串在进行操作的时候,区间外的元素必定不变

④ a 串中的元素是否反转,和此元素被选中在区间内的次数有关(奇数次翻转,偶数次不变)

⑤ b 串在进行操作的时候,区间内的元素必定不变

⑥ b 串中的元素是否反转,和此元素被选中在区间外的次数有关(奇数次翻转,偶数次不变)

⑦如果 a 串 b 串全是 1 或者全是 0 的话会比较特殊

所以初始大致思路如下:

一般情况下如果 a 串 b 串完全相反,先花一次操作(1,n)把 a 串取反,这样 a 串 b 串就一样了。然后把 a 串 b 串的所有数都变成 1 ,然后花 3 次操作全变成 0 ,即(1,1)(2,n)(1,n)

那么我们的问题就转变成了,如何将 ab 串都转变成全 1 的。详情可以观看下图

因为前面的操作确保了 a 串 b 串是完全相同的,我们可以先找到一段连续的 1 ,然后经过红色区间蓝色区间两次操作之后,我们会发现,我们只会对绿色区间里的元素进行转换,而绿色外面的元素经过两次操作会保持不变。这样我们就实现了将绿色区间里面的所有元素都转变成了 1 。在此基础上,我们不断找后面的连续的 1 ,就可以完成对整个 ab 串都转换成 1 的操作。

然后就像上面说的那样,花3次操作全变成 0 ,即(1,1)(2,n)(1,n)就可以将 ab 串都变成全 0 的。

emmm大致思路是这样的,具体操作请看初始思路AC代码。

可能代码略微复杂,但我认为这也是一种很好的思路,所以我还是想保留下来,如果看不懂这种思路的代码请移步下面更优化的思路及其代码

初始思路AC代码:

#include<iostream>
#include<vector>
using namespace std;

typedef long long ll;

const int N = 2e5 + 5;

char a[N];
char b[N];

int main()
{
	std::ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	int t;
	cin >> t;

	while (t--)
	{
		int n;
		cin >> n;

		cin >> a + 1;
		cin >> b + 1;

		int flag = a[1] == b[1];//a串b串第一个位置相同还是相反的状态
		int ooo = 0;//ab串是否完全相同或完全相反的标志

		for (int i = 1; i <= n; i++)
		{
			if ((a[i] == b[i]) != flag)
			{
				ooo = 1;
				cout << "NO" << '\n';
				break;
			}
		}
		if (ooo)continue;

		cout << "YES" << '\n';
		vector<pair<int, int>> ans;

		if (flag == false)
			ans.push_back({ 1,n });

		int f = 1;//(a串全为1,b全为0)(a全为0,b全为0)的时候f == 1

		for (int i = 1; i <= n; i++)
			if ((a[i] == '0') != flag)
			{
				f = 0;
				break;
			}

		if (f == 1)
		{
			cout << ans.size() << '\n';

			if (flag == false)
				cout << 1 << " " << n << '\n';
			continue;
		}

		int l, r;
		for (l = 1; l <= n; l++)if ((a[l] == '1') == flag)break;
		for (r = l; r <= n; r++)if ((a[r] == '0') == flag)break;

		ans.push_back({ 1,r - 1 });
		for (; r <= n; r++)if ((a[r] == '1') == flag)break;
		ans.push_back({ l,r - 1 });

		while (r <= n)
		{
			l = min(r, n);

			for (; r <= n; r++)if ((a[r] == '0') == flag)break;
			ans.push_back({ l,r - 1 });

			for (; r <= n; r++)if ((a[r] == '1') == flag)break;
			ans.push_back({ l,r - 1 });
		}


		ans.push_back({ 1,1 });
		ans.push_back({ min(2,n),n });

		cout << ans.size() << '\n';

		for (int i = 0; i < ans.size(); i++)
			cout << ans[i].first << " " << ans[i].second << '\n';

	}

	return 0;
}

更优化的思路:

所以我们总结以下,什么会影响 b 串?其一是对 a 的操作次数,其二是 a 串与 b 串是相同还是相反

所以我们可以简化以下思路,不用特意对 a 串中连续的 1 进行操作,直接对 a 串中每一个 1 进行单独操作也可以,会有大致以下四种情况:

① a 串中 1 的个数为奇数, a 串 b 串完全相同

② a 串中 1 的个数为奇数, a 串 b 串完全相同

③ a 串中 1 的个数为偶数, a 串 b 串完全相反

④ a 串中 1 的个数为奇数, a 串 b 串完全相反

对 a 串中所有 1 的位置进行单独操作后, a 串都会变成全 0 的状态,但是 b 串不一定,其中 ② 和 ④ 的 b 串会变成全 0 的状态,此时符合题意。 但 ① 和 ③ 的 b 串会变成全 1 的情况,此时还需要多做三步,即(1,1)(2,n)(1,n)。

那么思路有了,具体操作请看更优化的思路的AC代码

更优化的思路的AC代码:

#include<bits/stdc++.h>
using namespace std;


typedef long long ll;

const int N = 2e5 + 10;

char a[N];
char b[N];

int main()
{
	std::ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);


	int t; cin >> t;

	while (t--)
	{
		int cnt = 0;//操作次数
		vector<pair<int, int>>v;//操作步骤

		int n;
		cin >> n;

		cin >> a + 1;
		cin >> b + 1;

		int flag1 = 1;//ab串都相同
		int flag2 = 1;//ab串都相反

		for (int i = 1; i <= n; i++)
		{
			if (a[i] != b[i])flag1 = 0;
			if (a[i] == b[i])flag2 = 0;

		}
		
		if (flag1 || flag2)
			cout << "YES" << '\n';
		else
		{
			cout << "NO" << '\n';
			continue;
		}


		for (int i = 1; i <= n; i++)
		{
			if (a[i] == '1')
			{
				v.push_back({ i,i });
				cnt++;
			}
		}

		
		if ((flag1 && cnt % 2 == 1) || (flag2 && cnt % 2 == 0))
		{
			v.push_back({ 1,n });
			v.push_back({ 1,1 });
			v.push_back({ 2,n });

			cnt += 3;
		}

		cout << cnt << "\n";

		for (auto& [x, y] : v)
			cout << x << " " << y << '\n';

	}

	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值