题目:
题解:
其实是线性基的基本问题了。
线性基?下见普及向咯
实际上可以从线性基的定义及性质得知,x2的选取就是线性基的最大值,因为二进制位有1必取,这样做也并不担心x1会变得很小,因为只取一个1,剩下的1单数偶数是固定的,我们起码要保证最大的二进制位要取上啊
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#define LL long long
using namespace std;
const int N=100005;
const int sz=61;
LL a[N],ans1,ansh,lb[sz+5],ans2;int n;
void dfs(int t,LL x1,LL x2)
{
if (t>n)
{
if (x1+x2>ansh)
{
ansh=x1+x2;
ans1=min(x1,x2);
}else if (x1+x2==ansh) ans1=min(ans1,min(x1,x2));
return;
}
dfs(t+1,x1^a[t],x2);
dfs(t+1,x1,x2^a[t]);
}
int main()
{
freopen("divide.in","r",stdin);
freopen("divide.out","w",stdout);
LL sum=0;
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]),sum^=a[i];
if (n<=10)
{
dfs(1,0,0);
printf("%lld %lld",ans1,ansh-ans1);
}
else
{
for (int i=1;i<=n;i++)
{
LL t=a[i];
for (int j=sz;j>=1;j--)
if ((t>>j-1)&1)
{
if (!lb[j]){lb[j]=t;break;}
else t^=lb[j];
}
}
for (int i=sz;i;i--)
if ((ans2^lb[i])>ans2) ans2^=lb[i];
printf("%lld %lld",sum^ans2,ans2);
}
}
普及向:线性基
定义
设数集T的值域范围为
[1,2n−1]
[
1
,
2
n
−
1
]
T的线性基是T的一个子集A={a1,a2,a3,…,an}。
A中元素互相xor所形成的异或集合,等价于原数集T的元素互相xor形成的异或集合。
可以理解为将原数集进行了压缩。
性质
1.设线性基的异或集合中不存在0。
2.线性基的异或集合中每个元素的异或方案唯一,其实这个跟性质1是等价的。
3.线性基二进制最高位互不相同。
4.如果线性基是满的,它的异或集合为[1,2n−1]。
5.线性基中元素互相异或,异或集合不变。
维护
插入
如果向线性基中插入数x,从高位到低位扫描它为1的二进制位。
扫描到第i时,如果ai不存在,就令ai=x,否则x=x⊗ai。
x的结局是,要么被扔进线性基,要么经过一系列操作过后,变成了0。
for (int i=1;i<=n;i++)
{
LL t=a[i];
for (int j=sz;j>=1;j--)
if ((t>>j-1)&1)
{
if (!lb[j]){lb[j]=t;break;}
else t^=lb[j];
}
}
合并
将一个线性基暴力插入另一个线性基即可。
查询
存在性
如果要查询x是否存于异或集合中。
从高位到低位扫描x的为1的二进制位。
扫描到第i位的时候x=x⊗ai
如果中途x变为了0,那么表示x存于线性基的异或集合中。
过程就像添加失败的x一样啊
最大值
从高位到低位扫描线性基。
如果异或后可以使得答案变大,就异或到答案中去。
for (int i=sz;i;i--)
if ((ans2^lb[i])>ans2) ans2^=lb[i];
最小值
最小值即为最低位上的线性基。
long long query_min()
{
for (int i=0;i<=sz;i++)
if (lb[i]) return lb[i];
}
k小值
根据性质3。
我们要将线性基改造成每一位相互独立。
具体操作就是如果i < j,aj的第i位是1,就将aj异或上ai。
经过一系列操作之后,对于二进制的某一位i。只有ai的这一位是1,其他都是0。
所以查询的时候将k二进制拆分,对于1的位,就异或上对应的线性基。
最终得出的答案就是k小值。
void rebuild()
{
for (int i=60;i>=0;i--)
for (int j=i-1;j>=0;j--)
if (d[i]&(1LL<<j))
d[i]^=d[j];
for (int i=0;i<=60;i++)
if (d[i]) p[cnt++]=d[i];
}
long long kthquery(long long k)
{
int ret=0;
if (k>=(1LL<<cnt))
return -1;
for (int i=60;i>=0;i--)
if (k&(1LL<<i))
ret^=p[i];
return ret;
}