东学学西学学
定义
设一个数集 S S 通过异或可以组成的集合为,那么 S S 的线性基是的一个子集 A={p1,p2,…,pn} A = { p 1 , p 2 , … , p n } ,满足 px p x 的最高位1的位置为 x x ,且A中的数通过异或组成的数集也为。
线性基的维护
插入
每当需要插入一个数 x x 时,从高位到低位扫一遍。当扫到有一位为1时,如果此位线性基为0(就是还没有),就把 x x 赋给并退出扫描,否则 x=x xor pi x = x x o r p i ,继续扫描。
代码实现:
for (int i=1;i<=n;i++){
LL x; scanf("%lld",&x);
for (int j=50;~j;j--)
if ((x>>j)&1)
if (!p[j]) { p[j]=x; break; }
else x^=p[j];
}
查询
1.查询原数集能异或得到的最大值:
从线性基高位往低位扫,如果异或上当前值能变大就异或。
代码实现:
for (int i=50;~i;i--) if ((ans^p[i])>ans) ans^=p[i];
2.判断 x x 能否被异或出来:
从高位往低位扫,如果的这一位为1就异或上对应的 pi p i 。如果最后得到的数为0就可行。当然要特判 x x <script type="math/tex" id="MathJax-Element-19">x</script>为0的情况。
代码实现:
bool f=false;
if (x){
for (int i=50;~i;i--) if (1&(x>>i)) x^=p[i];
if (!x) f=true;
}
puts(f?"Yes":"No");
模板
以洛谷P3812为例:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 55
using namespace std;
typedef long long LL;
int n;
LL p[N],ans;
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++){
LL x; scanf("%lld",&x);
for (int j=50;~j;j--)
if ((x>>j)&1)
if (!p[j]) { p[j]=x; break; }
else x^=p[j];
}
for (int i=50;~i;i--)
if ((ans^p[i])>ans) ans^=p[i];
return printf("%lld\n",ans),0;
}