线性基笔记
线性基是一种在异或操作上有很大用处的数据结构。
可以求异或最值,区间异或最值的问题
可以用来水各种题
线性基的定义
1.线性基能相互异或得到原集合的所有相互异或得到的值。
2.线性基是满足性质1的最小的集合
3.线性基没有异或和为0的子集。
线性基的插入
二进制下拆分数x,从高位向低位扫
若x再当前位为1,就判断当前位线性基是否有值,
若有就把x的当前位消掉,否则就加入线性基
线性基的插入函数如下:
inline bool Insert(int x) {
drep(i,60,0)if(x&(1ll<<i)) {
if(d[i])x^=d[i];
else {
d[i]=x;
break;
}
}
return x>0;
}
查询异或最值
查询最小值相对比较简单。线性基中最小值异或上其他数,必定增大。所以,直接输出线性基中的最小值即可。
考虑异或最大值,从高到低遍历线性基,考虑到第&i&位时,如果当前的答案第&i&位为0,就将其异或上 ;
否则不做任何操作。显然,每次操作后答案不会变劣,最终的结果即为答案。
inline int Query_max () {
int res=0;
drep(i,63,0)Max(res,res^d[i]);
return res;
}
inline int Query_min () {
rep(i,0,63)if(d[i])return d[i];
}
查询异或第&k&小值
1.照例建立线性基
2.使得线性基中有且只有base[i]的第i位为1
3.记录所有有值的base[] 从低位到高位记为0~cnt,共cnt + 1个 (注:闭区间
这时线性基可以构成的数有(1 << cnt) + 1个,如果cnt + 1 < n的话 说明可以取零 这时可以构成的数有(1 << (cnt + 1))个
4.取第k小时,如果k大于可以构成的数的总数 那么无解
否则res是所有base[i] ((k - 1)的第i位为1) 的异或和
using namespace std;
#define int long long
#define u64 unsigned long long
#define u32 unsigned int
#define reg register
#define Raed Read
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,G,x) for(reg int i=(G).Head[x]; i; i=(G).Nxt[i])
inline int Read() {
int res = 0, f = 1;
char c;
while (c = getchar(), c < 48 || c > 57)if (c == '-')f = 0;
do res = (res << 3) + (res << 1) + (c ^ 48);
while (c = getchar(), c >= 48 && c <= 57);
return f ? res : -res;
}
template<class T>inline bool Min(T &a, T const&b) {
return a > b ? a = b, 1 : 0;
}
template<class T>inline bool Max(T &a, T const&b) {
return a < b ? a = b, 1 : 0;
}
const int N=1e3+5,M=1e5+5,mod=1e9+7;
bool MOP1;
int n,d[65],A[65],cnt;
bool MOP2;
inline bool Insert(int x) {
drep(i,60,0)if(x&(1ll<<i)) {
if(d[i])x^=d[i];
else {
d[i]=x;
break;
}
}
return x>0;
}
inline void build(void) {
drep(i,60,0) {
if(!d[i])continue;
drep(j,i-1,0) {
if(!d[j])continue;
if(d[i]&(1ll<<j))d[i]^=d[j];
}
}
rep(i,0,60)if(d[i])A[cnt++]=d[i];
}
inline int Find(int x) {
if(cnt<n)x--;
// debug(cnt);
if(x>=(1ll<<cnt))return -1;
int Ans=0;
ret(i,0,cnt)if(x&(1ll<<i))Ans^=A[i];
return Ans;
}
inline void _main(void) {
// cerr<<"M="<<(&MOP2-&MOP1)/1024.0/1024.0<<endl;
int T=Read(),Case=0;
while(T--) {
memset(d,0,sizeof d);
cnt=0,n=Read();
rep(i,1,n)Insert(Read());
build();
int m=Raed();
printf("Case #%d:\n",++Case);
rep(i,1,m)printf("%lld\n",Find(Raed()));
}
}
signed main() {
_main();
}
线性基求区间异或最值
与序列的同理,加上编号记录一波即可,若右端点最值编号在左端点右边即可更新
using namespace std;
#define int long long
#define reg register
#define rep(a,b,c) for(reg int a=(b),a##_end_(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_(c); a>=a##_end_; --a)
inline void Rd(int &res) {
res=0;
int flag=0;
char c;
while(c=getchar(),c<48||c>57)flag|=(c=='-');
do res=(res<<1)+(res<<3)+(c^48);
while(c=getchar(),c>=48&&c<=57);
flag&&(res=-res);
}
template<class T>inline bool Max(T &a,T b) {return a<b?a=b,1:0;}
template<class T>inline bool Min(T &a,T b) {return a>b?a=b,1:0;}
const int N=5e5+5,INF=0x3f3f3f3f,mod=998244353;
int n,m,P[25][N],Id[25][N];
inline void Insert(int x,int y){
int pre=y;
rep(i,0,20)P[i][y]=P[i][y-1],Id[i][y]=Id[i][y-1];
drep(i,20,0)if(x>>i&1){
if(!P[i][y]){
P[i][y]=x,Id[i][y]=pre;
return;
}
if(Id[i][y]<pre)swap(Id[i][y],pre),swap(P[i][y],x);
x^=P[i][y];
}
return;
}
inline int query(int L,int R){
int Ans=0;
drep(i,20,0)if((Ans^P[i][R])>Ans&&(Id[i][R]>=L))Ans^=P[i][R];
return Ans;
}
inline void solve(){
Rd(n);
int x;
rep(i,1,n)Rd(x),Insert(x,i);
Rd(m);
while(m--){
int L,R;
Rd(L),Rd(R);
printf("%lld\n",query(L,R));
}
}
signed main(){
solve();
}