ABC261E 题解

前言

题目传送门!

更好的阅读体验?

这题另外两篇题解写的啥啊,这里提供一个非常好理解的做法。

思路

对于这种一堆位运算的题目,我们很容易想到按位考虑

准备两个 tag,一个是 \(cov\) 表示是否有覆盖操作,一个是 \(rev\) 表示是否有反转操作。作用见下文。(为了方便描述,我直接把每一位上的树称作原数了)

首先看 \(\text{and}\) 的情况。我们分类:

  • 如果 \(a_i = 0\),那么不管原数是什么,到这一步,原数必定会变成 \(0\)。所以 \(cov=0\)\(rev\) 清空。
  • 如果 \(a_i = 1\),什么也不会发生。

\(\text{or}\) 的情况是类似的:

  • 如果 \(a_i = 0\),什么也不会发生。
  • 如果 \(a_i = 1\),那么不管原数是什么,到这一步,原数必定会变成 \(1\)。所以 \(cov=1\)\(rev\) 清空。

最后是稍有不同的 \(\text{xor}\) 操作。可以发现,这个操作不会产生覆盖,所以我们将会改变 \(rev\)

具体地,当 \(a_i = 1\),说明会进行一次反转,所以 \(rev\) 增加。


于是在每一步过后,我们都有当前的 \(cov\)\(rev\)

如果存在 \(cov\),那么可以理解为,今后的全部操作,原数都直接在这里开始。所以直接把原数变成 \(cov\)

覆盖完后直接 \(rev\) 即可。每一步计算完后存入 \(ans_i\) 的对应位置。

所以另外那两篇题解搞 \(\text{bitset}\) 和 DP 是在干什么?

代码

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 2e5 + 5;
int op[N], a[N], ans[N];
int main()
{
	int n, val;
	scanf("%d%d", &n, &val);
	for (int i = 1; i <= n; i++) scanf("%d%d", &op[i], &a[i]);
	for (int d = 0; d <= 30; d++)
	{
		bool sum = (val & (1 << d)); int cov = -114514, rev = 0;
		for (int i = 1; i <= n; i++)
		{
			bool x = (a[i] & (1 << d));
			if (op[i] == 1) {sum &= x; if (x == 0) cov = 0, rev = 0;}
			if (op[i] == 2) {sum |= x; if (x == 1) cov = 1, rev = 0;}
			if (op[i] == 3) {/*sum ^= x;*/ if (x == 1) rev++;}
			
			if (cov != -114514) sum = cov;
			if (rev & 1) sum ^= 1;
			if (sum) ans[i] += (1 << d);
		}
	}
	for (int i = 1; i <= n; i++) printf("%d\n", ans[i]);
	return 0;
}
posted @ 2023-02-19 09:37  liangbowen  阅读(52)  评论(0编辑  收藏  举报