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;
}