线性基笔记

线性基是一种在异或操作上有很大用处的数据结构。

可以求异或最值,区间异或最值的问题

可以用来水各种题

线性基的定义

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(); 
}
posted @ 2024-10-16 15:42  dsjkafdsaf  阅读(5)  评论(0编辑  收藏  举报