【LOJ6060】【2017 山东一轮集训 Day1 / SDWC2018 Day1】Set 线性基

题目大意

  给出 \(n\) 个非负整数,将数划分成两个集合,记为一号集合和二号集合。\(x_1\) 为一号集合中所有数的异或和,\(x_2\) 为二号集合中所有数的异或和。在最大化 \(x_1 + x_2\) 的前提下,最小化 \(x_1\)

  \(n\leq 100000,0\leq a_i\leq {10}^8\)

题解

  记 \(s=a_1\operatorname{xor} a_2\operatorname{xor} a_3\operatorname{xor} \cdots\operatorname{xor}a_n\)

  那么就是要在最大化 \(s\operatorname{xor}x_2+x_2\) 的前提下最大化 \(x_2\)

  如果对于一个二进制位 \(i\),如果 \(s\) 在这一位上的值为 \(0\),并且 \(x_2\) 在这一位上的值为 \(1\),那么就会对 \(s\operatorname{xor}x_2+x_2\)\(2^{i+1}\) 的贡献。

  如果对于一个二进制位 \(i\),如果 \(s\) 在这一位上的值为 \(1\),并且 \(x_2\) 在这一位上的值为 \(1\),那么就会对 \(x_2\)\(2^i\) 的贡献。

  那么把这些二进制位分成两部分,求线性基,然后随便搞搞贪心取就好了。

  时间复杂度:\(O(n\log 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\n",sum^ans);
	return 0;
}
posted @ 2018-06-21 16:38  ywwyww  阅读(498)  评论(0编辑  收藏  举报