「解题报告」[ARC145E] Adjacent XOR
2022 做的最后一道题——
还是很有意思的一个构造题。
upd. 2023.1.26 修了一个问题 感谢 @歌吟入梦 orz
思路
题目中要求让
观察数据范围,发现大约是
首先我们可以得到一个 Yes 的必要条件:每一个
事实上,这也是一个充分条件,下面我们来尝试构造。
首先原数组看起来就很不好考虑,我们可以对它们求一个线性基,然后给基底按照加入的先后顺序标一个号,再用这组基底新的标号来表示
此时我们要构造的目的还不明确。但是注意到我们是从
可以发现,由于我们按照加入的先后顺序进行标号,那么如果第
考虑如何将前缀和的某一位翻转。我们知道
那么我们就有了一个构造方法:从大到小枚举每一位
将这个过程重复
Code
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int n;
long long a[MAXN], b[MAXN], c[MAXN];
int pos[64];
vector<int> ans;
struct LinearBasis {
long long a[63];
int id[63];
int tot;
bool contains(long long x) {
for (int i = 59; i >= 0; i--) if (x >> i & 1) {
if (a[i]) {
x ^= a[i];
} else {
return false;
}
}
return true;
}
long long insert(long long x) {
long long ret = 0;
for (int i = 60; i >= 0; i--) if (x >> i & 1) {
if (a[i]) {
ret |= (1ll << id[i]);
x ^= a[i];
} else {
a[i] = x;
id[i] = tot++;
return ret | 1ll << (id[i]);
}
}
return ret;
}
void clear() {
memset(a, 0, sizeof a);
memset(id, 0, sizeof id);
tot = 0;
}
} lb;
void filp(int x) {
ans.push_back(x);
for (int i = 1; i <= x; i++) {
b[i] ^= b[i - 1];
c[i] ^= c[i - 1];
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
for (int i = 1; i <= n; i++) {
scanf("%lld", &b[i]);
}
for (int i = 1; i <= n; i++) {
if (!lb.contains(a[i] ^ b[i])) { // 判断是否有解
printf("No\n");
return 0;
}
lb.insert(b[i]);
}
for (int i = n; i >= 1; i--) {
lb.clear();
for (int j = 1; j <= i; j++) {
bool isIn = lb.contains(b[j]); // 是否在线性基里,如果不在那就是有新的基底,记录第一次出现位置 pos_i
c[j] = lb.insert(b[j]);
if (!isIn) {
pos[__lg(c[j])] = j;
}
}
long long goal = lb.insert(a[i]);
for (int j = lb.tot - 1; j >= 0; j--) {
long long sum = 0;
for (int k = 1; k <= i; k++) sum ^= c[k];
if ((sum >> j & 1) != (goal >> j & 1)) filp(pos[j] + 1);
}
filp(i);
}
reverse(begin(ans), end(ans));
printf("Yes\n%lu\n", ans.size());
for (int i : ans) printf("%d ", i);
printf("\n");
return 0;
}