异或路径
题目链接:YBT2023寒假Day9 A
题目大意
有一个 n*m 的网格,然后你要从 (1,1) 走到 (n,m),你的分数是你路径上每个点的权值的异或和(出现多次就异或多次)。
每次可以选上下左右四个方向走,不能走出网格,问你分数最大值。
思路
首先如果只能向右向下走你走的点的个数是确定是
n
+
m
−
1
n+m-1
n+m−1。
考虑在这个基础上加一些修改,会发现你如果要改一段路径,你把那一段的路径,和你要改成的路径,它形成一个圈,那你直接异或上这个圈的值,就可以改路径了。
那么这样的话,我们其实可以直接控制任意个数是否出现。
但是其实是错的,你会注意到个数不是任意的。
考虑每个圈的长度都是偶数,而且有相同位置就消去每次也是消除
2
2
2 个(偶数个),所以你无论怎么变换,个数的奇偶性都是
n
+
m
−
1
n+m-1
n+m−1 的奇偶性。
于是考虑如何让线性基选的数量的奇偶性固定。
于是考虑让选奇数个更优或者偶数个更优。
那联系上异或的性质,对于选奇数个我们可以给每个数赋一个很大的二次方值(比如
2
45
2^{45}
245),然后再丢进去跑线性基,出来的结果异或一下这个值即可。
那对于偶数,那你就在跑最大值的时候一开始是这个很大的数,就跟奇数一样了。
代码
#include<cstdio>
#define ll long long
using namespace std;
const int N = 5e5 + 100;
int n, m, tot;
ll a[N];
struct XXJ {
ll f[64];
void insert(ll x) {
for (int i = 63; i >= 0; i--)
if ((x >> i) & 1) {
if (!f[i]) {
f[i] = x; break;
}
x ^= f[i];
}
}
ll get_max(ll fir) {
for (int i = 63; i >= 0; i--) {
if (!((fir >> i) & 1)) fir ^= f[i];
}
return fir;
}
}p;
int main() {
freopen("xor.in", "r", stdin);
freopen("xor.out", "w", stdout);
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) scanf("%lld", &a[++tot]), a[tot] ^= (1ll << 45), p.insert(a[tot]);
if (!((n + m - 1) & 1)) printf("%lld", p.get_max(1ll << 45) ^ (1ll << 45));
else printf("%lld", p.get_max(0ll) ^ (1ll << 45));
return 0;
}