【YBT2023寒假Day9 A】异或路径(线性基)

异或路径

题目链接:YBT2023寒假Day9 A

题目大意

有一个 n*m 的网格,然后你要从 (1,1) 走到 (n,m),你的分数是你路径上每个点的权值的异或和(出现多次就异或多次)。
每次可以选上下左右四个方向走,不能走出网格,问你分数最大值。

思路

首先如果只能向右向下走你走的点的个数是确定是 \(n+m-1\)
考虑在这个基础上加一些修改,会发现你如果要改一段路径,你把那一段的路径,和你要改成的路径,它形成一个圈,那你直接异或上这个圈的值,就可以改路径了。

那么这样的话,我们其实可以直接控制任意个数是否出现。
但是其实是错的,你会注意到个数不是任意的。
考虑每个圈的长度都是偶数,而且有相同位置就消去每次也是消除 \(2\) 个(偶数个),所以你无论怎么变换,个数的奇偶性都是 \(n+m-1\) 的奇偶性。

于是考虑如何让线性基选的数量的奇偶性固定。
于是考虑让选奇数个更优或者偶数个更优。
那联系上异或的性质,对于选奇数个我们可以给每个数赋一个很大的二次方值(比如 \(2^{45}\)),然后再丢进去跑线性基,出来的结果异或一下这个值即可。
那对于偶数,那你就在跑最大值的时候一开始是这个很大的数,就跟奇数一样了。

代码

#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;
} 
posted @ 2023-02-08 19:03  あおいSakura  阅读(11)  评论(0编辑  收藏  举报