线性基构造方法
对于每一个数,我们找出他的最高位的 1 在第 i 位, 如果此时 Pi 为零,就将这个数加入线性基,否则异或 Pi 继续找。然后我们就可以在 0 到 k 位上处理好每一位的线性基。这样得到的线性基保证每一位都能有对应的最大值。
void get_p(LL x) { for(int i=62;i>=0;i--) { if((x>>(LL)i)) { if(!p[i]) { p[i]=x; break; } x^=p[i]; } } }
求最大异或和
在我们得到的线性基中,从高位到低位用贪心贪掉每一个元素,如果异或了这个元素变大就异或他。
LL ans=0; for(int i=62;i>=0;i--) if((ans^p[i])>ans)ans^=p[i];
查询某个数
我们也是从低到高扫这个数的每一位,如果这第 i 位为 1,就异或上 Pi,然后知道处理到最后一位。如果变成 0 了,那么就是可以的。
bool check(int x)//查询能否异或成某个数 { for(int i=31;i>=0;i--) { if(x>>i)x^=p[i]; } return x==0; }
线性基求交
有一道模板题,不过要用到线段树来优化查询
题目链接:https://ac.nowcoder.com/acm/contest/884/B
#include<bits/stdc++.h> using namespace std; const int maxx=5e4+10; int a[maxx<<2][32]; void insert(int x,int temp)//构造线性基 { for(int i=31;i>=0;i--) { if(x>>i) { if(!a[temp][i]) { a[temp][i]=x; break; } x^=a[temp][i]; } } } bool check(int x,int temp)//查询能否异或成某个数 { for(int i=31;i>=0;i--) { if(x>>i)x^=a[temp][i]; } return x==0; } void pushup(int temp)//线性基求交 { int t1[32],t2[32],l=temp*2,r=temp*2+1; for(int i=0;i<32;i++) t1[i]=t2[i]=a[l][i]; for(int i=0;i<32;i++) { if(a[r][i]) { int x=a[r][i],t=0; for(int j=31;j>=0;j--) { if(x>>j) { if(!t1[j]) { t1[j]=x,t2[j]=t; break; } x^=t1[j],t^=t2[j]; } } if(!x)insert(t,temp); } } } void build(int l,int r,int temp) { if(l==r) { int s,t; scanf("%d",&s); while(s--) { scanf("%d",&t); insert(t,temp); } return; } int mid=(l+r)/2; build(l,mid,temp*2); build(mid+1,r,temp*2+1); pushup(temp); } bool query(int l,int r,int p,int q,int x,int temp) { if(p<=l&&r<=q) { return check(x,temp); } int mid=(l+r)/2; bool res=1; if(p<=mid)res&=query(l,mid,p,q,x,temp*2); if(q>mid)res&=query(mid+1,r,p,q,x,temp*2+1); return res; } int main() { int n,m; cin>>n>>m; build(1,n,1); int p,q,x; while(m--) { scanf("%d%d%d",&p,&q,&x); if(query(1,n,p,q,x,1))printf("YES\n"); else printf("NO\n"); } return 0; }