题解

思维题

一开始觉得比较像最大XOR路径

但是这里是点权

考虑到是网格图,我们就可以来研究一下它的性质

如果从1,1出发,随意地走一条路径,再按原路返回

发现我们最后的贡献就是任意一个格子

换句话说,我们每走一步,都可以选择任意一个格子的权值,异或到答案上

因为两次可以选同样的值,所以我们选出来的格子数一定是与n+m-1同奇偶的

问题就可以转化为:给出一个数组,选出奇数/偶数个数,使它们的异或和最大

我们可以把(奇数个数的异或和)拆成(偶数个数异或和^某一个数)

于是我们就来解决偶数个数异或起来最大的最大值

我们很显然,可以把所有的数两两异或算出来

但是这样做肯定会超时

于是我们想到了随机化

随机两个数,把他们的异或和加入线性基

应该就可以AC了

哦哦,还有一个优化,先把所有的数排一个序,先把最小的数依次异或上每一个数的值加入线性基

因为最小的数对于其它值的影响是最小的,所以这么一来,线性基就基本上被填满了

然后random_shuffle一下,用n^2来把所有的两两异或的值加入线性基,当线性基满了就break

大功告成!!!

 

以上就是我考试时的思路,然后我把它写了出来,信心满满想要AC

然后考完一看

啊啊啊啊啊啊,为什么只有30pts

一看代码

用最小值异或其他值的范围只开了n,本来应该是n*m的

把范围改大,测了一下

还是30pts

啊啊啊啊啊啊,怎么办怎么办怎么办

Oh,还有这一手,换一种随机方式,保证可以AC

于是把random_shuffle后的n^2改成了O(n)扫一遍过去,把相邻两个数的异或值加入线性基

再测一下

A   C   了

啊啊啊啊啊啊,没切掉签到题,好难受,啊啊啊啊啊啊

 

看了一下题解

发现比我的想法更巧妙

哪里用得着随机化啊。。。

唉,智商余额不足啊

但是随机化写起来简单一些吧(e,应该是好想一些)

 

我的代码:(随机化+线性基)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 505
int a[N][N],b[N*N];
int base[33],siz,len;
void add(int x)
{
	for(int i=len-1;i>=0;i--)if((x>>i)&1){
		if(!base[i]){base[i]=x;siz++;return;}
		x^=base[i];
	}
}
int query(int x)
{
	for(int i=len-1;i>=0;i--)
		x=max(x,x^base[i]);
	return x;
}
int main()
{
	freopen("sign.in","r",stdin);
	freopen("sign.out","w",stdout);
	int n,m,i,j,mx=0;
	n=gi();m=gi();
	for(i=1;i<=n;i++)
		for(j=1;j<=m;j++){
			b[(i-1)*m+j]=a[i][j]=gi();
			mx=max(mx,a[i][j]);
		}
	while(mx)len++,mx>>=1;
	sort(b+1,b+n*m+1);
	for(i=1;i<=n*m;i++)add(b[1]^b[n]);
	random_shuffle(b+1,b+n*m+1);
	for(i=1;i<n*m&&siz<len;i++)
		add(b[i]^b[i+1]);
	if((n+m-1)&1){
		int ans=0;
		for(i=1;i<=n;i++)
			for(j=1;j<=m;j++)
				ans=max(ans,query(a[i][j]));
		printf("%d",ans);
	}
	else printf("%d",query(0));
}

 

 

std:(巧妙的线性基)(好短的代码)

#include<cstdio>
#define K 30
int b[K+1];
int main()
{
	freopen("sign.in","r",stdin);
	freopen("sign.out","w",stdout);
	int n,m,x,i,j;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n*m;++i)
	{
		scanf("%d",&x);x|=1<<K;
		for(j=K;~j;--j)if(x&(1<<j))x^=b[j]=b[j]?b[j]:x;
	}
	for(x=((n+m)&1)<<(j=K);~j;--j)if(~x&(1<<j))x^=b[j];
	printf("%d",x&((1<<K)-1));
}

 

 

唉,老年选手的日常爆炸