题目大意
给出 (n) 个非负整数,将数划分成两个集合,记为一号集合和二号集合。(x_1) 为一号集合中所有数的异或和,(x_2) 为二号集合中所有数的异或和。在最大化 (x_1 + x_2) 的前提下,最小化 (x_1)。
(nleq 100000,0leq a_ileq {10}^8)
题解
记 (s=a_1operatorname{xor} a_2operatorname{xor} a_3operatorname{xor} cdotsoperatorname{xor}a_n)。
那么就是要在最大化 (soperatorname{xor}x_2+x_2) 的前提下最大化 (x_2)。
如果对于一个二进制位 (i),如果 (s) 在这一位上的值为 (0),并且 (x_2) 在这一位上的值为 (1),那么就会对 (soperatorname{xor}x_2+x_2) 有 (2^{i+1}) 的贡献。
如果对于一个二进制位 (i),如果 (s) 在这一位上的值为 (1),并且 (x_2) 在这一位上的值为 (1),那么就会对 (x_2) 有 (2^i) 的贡献。
那么把这些二进制位分成两部分,求线性基,然后随便搞搞贪心取就好了。
时间复杂度:(O(nlog V))
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<functional>
#include<cmath>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
ll rd(){ll s=0;int c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
ll a[100010];
ll s[100];
ll sum;
int n;
void insert(ll v)
{
for(int i=62;i>=0;i--)
if(!((sum>>i)&1)&&((v>>i)&1))
{
if(!s[i])
{
s[i]=v;
return;
}
v^=s[i];
}
for(int i=62;i>=0;i--)
if(((sum>>i)&1)&&((v>>i)&1))
{
if(!s[i])
{
s[i]=v;
return;
}
v^=s[i];
}
}
int main()
{
open("loj6060");
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
a[i]=rd();
sum^=a[i];
}
for(int i=1;i<=n;i++)
insert(a[i]);
ll ans=0;
for(int i=62;i>=0;i--)
if(!((sum>>i)&1)&&((ans^s[i])+(sum^ans^s[i])>ans+(sum^ans)||((ans^s[i])+(sum^ans^s[i])==ans+(sum^ans)&&(ans^s[i])>ans)))
ans^=s[i];
for(int i=62;i>=0;i--)
if(((sum>>i)&1)&&((ans^s[i])+(sum^ans^s[i])>ans+(sum^ans)||((ans^s[i])+(sum^ans^s[i])==ans+(sum^ans)&&(ans^s[i])>ans)))
ans^=s[i];
printf("%lld
",sum^ans);
return 0;
}