题目链接: Co-growing Sequence
大致题意
定义:
-
一个长度为n的序列a, 如果满足 a[i] & a[i + 1] == a[i], i ∈ [1, n), 则称该序列为增长的
-
两个长度均为n的序列a, b. 现构造出长度为n的序列c, 使得 c[i] = a[i] ⊕ b[i], i ∈ [1, n], 若c序列是增长的, 则称序列a和b是同增的
现给出序列a, 让你构造出一个字典序最小的序列b, 使得a和b是同增的.
解题思路
我们首先对增长的序列进行分析:
如果a[i] & a[i + 1] == a[i], 则表明a[i]二进制位为1的位置, a[i + 1]在该位上也要为1.
考虑到题中需要我们构造的序列b, 相当于我们可以修改a[i], 使其变为c[i]后, 让c序列变为增长的序列.
相当于a[i] & a[i + 1] == a[i]
, 变为: (a[i] ⊕ b[i]) & (a[i + 1] ⊕ b[i + 1]) == a[i] ⊕ b[i]
<==> c[i] & c[i + 1] == c[i]
.
由于b序列要求字典序最小, 因此最初i = 1
时, 我们令b[1] = 0, 找到最小的b[2]一定最优的. 当i = 2
时, 此时b[2]的值确定了, 相当于c[2]确定了, 此时我们找到最小的b[3]一定最优. 以此类推……
我们发现, 由于每次我们都是找到最小的b[i + 1]最优, 因此我可以将上式改写为: c[i] & (a[i + 1] ⊕ b[i + 1]) == c[i]
, 其中c[i], a[i + 1]都是确定的.
因此题目等价于: 我们找到最小的b[i + 1], 使得temp = a[i + 1] ⊕ b[i + 1]
, 满足c[i]二进制位为1的位置, temp在该位上也为1. (文段最开头提到了)
我们考虑如何找到这个temp值. 提取模型 x & (y ⊕ z) == x 找到最小的z
这相当于, 使得 (y ⊕ z) == x | y 等价于 z = (x | y) ⊕ y
为什么呢?
我们对于每一位来考虑:
①如果x和y本身这一位上相同, 则z这一位上取0即可.
②如果这一位不相同: 如果x是0, y是1, 则z这位取0; 反之若x是1, y是0, 则z这位取1.分析可得: z的作用本质是把x为1的位置, y为0的位置变为1. 至于其余的情况, 都无需考虑. 而(x | y) ⊕ y刚好就做了这样的事情.
因此取b[i + 1] = (c[i] | a[i + 1]) ^ a[i + 1]
即可.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 2E5 + 10;
int a[N], b[N];
int main()
{
int t; cin >> t;
while (t--) {
int n; scanf("%d", &n);
rep(i, n) scanf("%d", &a[i]);
rep(i, n) { // 代码中, a序列与c序列共用了a[]数组, a[j]即为c[j], j < i.
b[i] = (a[i - 1] | a[i]) ^ a[i];
a[i] ^= b[i]; //把a[i]变为c[i]
}
rep(i, n) printf("%d%c", b[i], " \n"[i == n]);
}
return 0;
}