不知道最初是从哪个角度发现的
蒟蒻我最初是从玄学的线性代数与向量思想开始学的,然后因为不会高斯消元转入另一种。
其实对于线性基来说,只要搞懂一个关系式子:axorb=c,cxora=b,bxorc=a;axorb=c,cxora=b,bxorc=a;
也就是说,知二求一。那么将一堆数合成一个线性基,应用的也是这个道理;
线性基长什么样
就是一个一维数组,a[i]a[i]等于的数满足该数二进制表示下,最高有一的那一位为i;比如a[3]=(110)2a[3]=(110)2
下面具体讲操作
给你一个线性基,给你一个数,问这个数是否属于该线性基的张成。
最简单的想法:2^n枚举;但这显然过于暴力。
这样想:如果属于,那么用线性基异或出来的数异或这个数等于0。我们只要让这个数去异或线性基中的数,使它竭力靠近零。
如何竭力靠近0?
举个例子:一个数为(1011001101)2(1011001101)2,然后发现了第十位为一。最终结果第十位不为1,肯定要异或掉就拿a[10]a[10]来异或。这样就对10位以上的更高位保持为0没有影响.然后再看第九位.
唯一值得注意的,本身是零要特判。
给你一堆数构造他们的线性基。
这一个操作实际上结合了定义之后就变得简单。只要你加入的这一个数有一的最高位在原线性基中还没有出现,直接赋值即可。如果出现了,那么就与这一位异或一下。
讲的可能不太清楚,上一份代码就懂了
for(int i=1;i<=n;i++) {
for(int j=62;j>=0;j--) {
if(!(a[i]>>j)) continue;//对线性基的这一位没有贡献
if(!p[j]) { p[j]=a[i]; break; }//选入线性基中
a[i]^=p[j];
}
}
给你一个线性基要你求能构造出来的第k小
这一个的做法其实有点利用二进制思想。如果现在有(1)2,(10)2,(100)2,(1000)2,(10000)2,(100000)2....(1)2,(10)2,(100)2,(1000)2,(10000)2,(100000)2....这些数,要你构造他们异或和的第k大,你可能会觉得这太容易了。这是什么原理呢??掘其本质,你会发现他们无论怎么异或,原有位置上的1永远不会异或变成零,这又是由于如果他们有一位为一,其它数这个位上绝对不为一。如果我们的线性基有这一个性质就好了。也就是说,按定义a[i]a[i]表示有一的最高位是ii,那如果其余的任意的第i位都为0,那就满足了a[i]a[i]最高位上的i怎么也不会异或成零。
现在最重要的一点是如何构造?假设现在这个线性基已经构造好了,由于对于a[i],a[k],i>ka[i],a[k],i>k,a[k]a[k]显然不会影响a[i]a[i]只要扫两遍就行了
上个代码
il void rebuild(){ for (RG ll i=62;i>=0;--i) for (RG ll j=i-1;j>=0;--j) if (p[i]>>j&1) p[i]^=p[j]; for (RG ll i=0;i<=62;++i) if (p[i]) [cnt++]=p[i]; return; } il ll query_kth(RG ll k){ if (k>=(1LL<<cnt)) return -1; RG ll res=0; for (RG ll i=62;i>=0;--i) if (k>>i&1) res^=d[i]; return res; }
最后讲一讲个人搞出来的时间戳线性基。
如果有nn个数依次加入线性基,在加入过程中,不断询问,区间[l,r][l,r]的数能异或出来的最大的数是多少。遮盖怎么做?
这就需要再次仔细研究线性基的性质。这次我们要考虑加入的时间。现在加入a,b,c,d,ea,b,c,d,e五个数,加入到d时,询问b,c,db,c,d能够异或的最大数是多少。
这时就显然不能考虑aa的影响。在这里还要插入一个小技巧,就是把询问区间按左端点与右端点排序。这样你的询问就是有序的了(嗯,这个方法真的常用)。
我们这样做后 显然 要使得
- 排除线性基中所有被 影响了的数
- 剩下的这些数中是b,c,db,c,d的线性基
我们的任务是这两点。乍一看,不可能实现,实际上由于加入是一个不可逆的过程,我们无法用线性基去删除一个元素,而询问区间是有序的,对每一个线性基中的数打上一个时间戳就显得尤为重要。
我们不妨设线性基中的每一个数都有一个时间戳dd 这个代表的意义是:影响该位的最小位置为dd
有了这个时间戳后,被小位置更新过的一定不包含被大位置更新过。
每次加入一个数后,直接处理以该数位置为右端点的所有询问。
详细见代码
#include<bits/stdc++.h>
using namespace std;
inline char gc(){
static char buf[1<<6],*p1=buf,*p2=buf;
return (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<6,stdin),p1==p2))?EOF:*p1++;
}
template <class T>
inline void read(T&data){
register char ch=0;
data=0;
while(ch<'0'||ch>'9')ch=gc();
while(ch<='9'&&ch>='0'){
data=(data<<3)+(data<<1)+(ch^48);
ch=gc();
}
return ;
}
const int _ (1e6+5);
int n,Time[_],L[_],head[_],nt[_],a[_],poww[_],Xor[32],ans[_],m;
int main(){
freopen("e.in","r",stdin);
read(n);
read(m);
for(register int i=1;i<=n;++i){
read(a[i]);
}
for(register int i=1,R;i<=m;++i){
read(L[i]);
read(R);
nt[i]=head[R],head[R]=i;
}
for(register int i=1;i<=4;++i){
register int I=i;
for(register int j=30;~j;--j){
if((1<<j)&a[i]){
if(!Xor[j]){
Xor[j]=a[i];
Time[j]=I;
break;
}
if(Time[j]<I){
swap(Time[j],I);
swap(Xor[j],a[i]);
}
a[i]^=Xor[j];
}
}
for(register int j=head[i];j;j=nt[j]){
for(register int k=30;~k;--k){
if(Time[k]>=L[j])ans[j]=max(ans[j],ans[j]^Xor[k]);
}
}
}
for(register int i=1;i<=m;++i){
cout<<ans[i]<<endl;
}
}