P3812 【模板】线性基
题目描述
给定 \(n\) 个整数(数字可能重复),求在这些数中选取任意个,使得他们的异或和最大。
输入格式
第一行一个数 \(n\),表示元素个数
接下来一行 \(n\) 个数
输出格式
仅一行,表示答案。
提示
$ 1 \leq n \leq 50, 0 \leq S_i < 2 ^ {50} $
Solution:
想写P3857 [TJOI2008] 彩灯 的解题报告,但是发现我线性基模板题的解题报告都没写。所以我来补线性基了捏。
先要知道线性基是什么,它通常用来解决一些数任选 \(k\) 个的最大异或和。我们发现这个问题显然是满足贪心的,我们只需要让高位尽可能是1就好了,我们利用这个性质来构造一个数组 \(base\) 表示如果希望这一位 \(i\) 取 1 ,那么就应该进行 \(ans\) XOR $ base_i$ 这一操作。
然后我们再来说一下 \(base\) 怎么构造:
在插入一个数 \(x\) 时,我们分为两种情况:
如果 \(base_i=0\) 那么 \(base_i=x\)
但是如果 \(base_i!=0\),x仍然会对更低位有贡献,但这个贡献并非 \(x\),而是 \(x\) XOR \(base\)。我们来思考一下为什么:
线性基的本质其实是将这些数的本质不同的 XOR 和用一个 n 维的基底表示出来,那么为了满足贪心,我们应该保证基底的每一维 \(base_i\) 不会对高位产生影响。在不考虑高位影响的情况下,在第 \(i\) 位有两个数 \(x,y\) 只考虑他们在 \([1,i]\) 位的影响,所以认为 \([i+1,n]\) 位全是0。然后假设 \(base_i,base_j,j<i\) 为x,y对于 \(base\) 的贡献那么我们可能产生的贡献可以表达为:
\(x=base_i\)
\(x\) XOR \(y\) \(=base_j\)
\(y=base_i\) XOR \(base_j\)
又因为异或运算满足结合律和交换律,所以我们就可以认为为上述式子对于两个及以上的数构造线性基时都是成立的。也就是说,现在我们通过线性基构造出了所有可能的异或和的结果。然后我们只需要在答案统计时将 \(ans\) 与 \(ans\) XOR \(bsse_i\) 取 max就好了。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=100;
int n;
int d[N],qpow[N];
void init()
{qpow[0]=1;for(int i=1;i<=60;i++){qpow[i]=qpow[i-1]<<1;}
}
void ins(int x)
{for(int i=60;i>=0;i--){if(x&qpow[i]){if(!d[i]){d[i]=x;break;}else {x^=d[i];}}}return ;
}
int query_max()
{int ans=0; for(int i=60;i>=0;i--){if((ans^d[i])>ans)ans=ans^d[i];}return ans;
}
void work()
{init();cin>>n;for(int i=1,x;i<=n;i++){scanf("%lld",&x);ins(x);}int ans=query_max();printf("%lld",ans);
}
#undef int
int main()
{work();
}