省選模擬賽20200417、A、簽到(線性基+隨機化)

 

 

題解

思維題

一開始覺得比較像最大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));
}

 

 

唉,老年選手的日常爆炸

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章