题意:给你一些数,问你是否能够将它们划分成两个集合,使得这两个集合的异或和之差的绝对值最小。
设所有数的异或和为S,集合A的异或和为A。
首先,S的0的位对答案不造成影响。
S的最高位1,所对应的A的那一位一定可以为1,不妨设它为1。
然后考虑后面的S的1位,尽量使A对应的位置为0,这样才能使S xor A,即B的值最大化,最接近A。
用线性基来进行判定,看能否将最高位到目前这位(假定目前这位是0)的这个区间用给定的数线性表出,如果能,就将这位设成0,否则,就将这位设成1。
妈的,其实整个过程只需要取出最大的线性基,然后尽量用较小的线性基去消掉除了最高位以外的1即可,得到的就是A!
队友的代码:
#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 */