【線性基】Petrozavodsk Winter Training Camp 2018 Day 1: Jagiellonian U Contest, Tuesday, January 30, 2018

題意:給你一些數,問你是否可以將它們劃分紅兩個集合,使得這兩個集合的異或和之差的絕對值最小。c++

設全部數的異或和爲S,集合A的異或和爲A。spa

首先,S的0的位對答案不形成影響。blog

S的最高位1,所對應的A的那一位必定能夠爲1,不妨設它爲1。get

而後考慮後面的S的1位,儘可能使A對應的位置爲0,這樣才能使S xor A,即B的值最大化,最接近A。it

用線性基來進行斷定,看可否將最高位到目前這位(假定目前這位是0)的這個區間用給定的數線性表出,若是能,就將這位設成0,不然,就將這位設成1。io

媽的,其實整個過程只須要取出最大的線性基,而後儘可能用較小的線性基去消掉除了最高位之外的1便可,獲得的就是A!class

隊友的代碼:集合

#include <bits/stdc++.h>
using namespace std;
#define FOR(i,a,b) for (LL i=(a);i<=(b);++i)
#define ROF(i,b,a) for (LL i=(b);i>=(a);--i)
typedef long long LL;
LL read(){
	LL x=0,f=1; char ch=getchar();
	while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
	while (ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); }
	return x*f;
}

const LL MAXN=300005;
LL n,m,q,a[MAXN],b[MAXN],c[MAXN],f[100];
int main() {
	LL T=read();
	while (T--) {
		n=read(); m=0;
		FOR(i,1,n) m^=a[i]=read();
		FOR(i,1,n) a[i]&=m;
		memset(f,0,sizeof(f));
		FOR(i,1,n) {
			ROF(j,62,0)
			if ((a[i]>>j)&1) {
				if (!f[j]) { f[j]=a[i]; break; }
				else {
					//if (a[i]<f[j]) swap(a[i],f[j]);
					a[i]^=f[j];
				}
			}
		}
		LL x=-1,y=0;
		ROF(i,62,0) if (f[i]) { x=i; break; }
		if (x>=0) y=f[x];
		//cerr<<y<<' '<<m<<endl;
		ROF(i,x-1,0)
		if (f[i])
			if ((y>>i)&1) y^=f[i];
		cout<<abs(y-(y^m))<<endl;
	}
	return 0;
}

/*
2
4
1 2 3 4
5
3 7 3 9 5

*/
相關文章
相關標籤/搜索