线性基

线性基性质:

一个数组集合中,一些元素相互异或 能得到原数组中的 任意元素

线性基的应用:

  1. 一个数组中,寻找最大异或和 or 最小异或和
  2. 一个数组中,第K小异或和
  3. 看一个数字是否能被线性基 异或 得到
  4. [L , R] 区间内,最大异或和

线性基应用的对应解法

  1. 最大异或和
  2. 第K小异或和
    /*
    引自————https://blog.csdn.net/Jasmineaha/article/details/100015215
    */
    using namespace std;
    typedef long long LL;
    const int Maxn = 1e6 + 5;
    const int Inf = 1e9 + 7;
    const int Over = 62;
    int N , Q;
    typedef long long LL;
    
    LL a[Maxn], f[70];
    int n, m, cnt = 0;
    
    void guass() {
    	for(int i = Over; i >= 0; i--) f[i] = 0LL;
    	for(int i = 1; i <= n; i++) {
    		for(int j = Over; j >= 0; j--) {
    			if((a[i] >> j) & 1) {
    				if(!f[j]) {
    					f[j] = a[i];
    					break;
    				}
    				a[i] ^= f[j];
    			}
    		}
    	}
    	for(int i = Over; i >= 0; i--) {
    		for(int j = i + 1; j <= Over; j++)
    			if((f[j] >> i) & 1) f[j] ^= f[i]; // 只有f[i]的第i位为1
    	}
    	cnt = 0;
    	for(int i = 0; i <= Over; i++) 
    		if(f[i]) f[cnt++] = f[i];
    }
    
    LL getKth(LL k) {
    	if(cnt < n) { // 说明原序列可以异或出0
    		if(k == 1) return 0; // 直接返回最小值0
    		else k--; // 去掉0这个最小值后的第K小
    	}
    	if(k >= (1LL << cnt)) return -1;
    	LL ans = 0LL;
    	for(int i = 0; i <= Over; i++) {
    		if((k >> i) & 1) ans ^= f[i];
    	}
    	return ans;
    }
    
    int main() 
    {
    	int T;scanf(" %d",&T);
    	int cas = 0
    	while(T--){
    		printf("Case #%d:\n", ++cas);
    		scanf("%d", &n);
    		for(int i = 1; i <= n; i++) scanf("%lld", a + i);
    		guass();
    		scanf("%d", &m);
    		while(m--) {
    			LL k; scanf("%lld", &k);
    			printf("%lld\n", getKth(k));
    		}
    	}
    	return 0;
    }

     

  3. 能否被线性基异或得到——将此数字插入线性基,能插入则说明不能得到,不能插入则能得到
  4. [L, R]  区间内的最大异或和——HDU6579
    /*维护维护每个点的前缀线性基,线性基里将靠右的数字尽可能放高位,就是存一个额外存一个位置 p,表示这个位上的数的位置,从高位到低位扫,如果当前位置大于这个位上的位置那么交换,然后就得到了一个靠右的数字尽可能在高位的线性基
    然后对于询问 [l,r] 在 r 的前缀线性基里找,只在位置大于等于 l 的位更新答案
    ————————注解 & 代码 引自https://www.cnblogs.com/xay5421/p/11228086.html
    */
    using namespace std;
    typedef long long LL;
    const int Maxn = 1e6 + 5;
    const int Inf = 1e9 + 7;
    const int Over = 32;
    int N , Q;
    int a[Maxn];
    int lastans;
    int sum[Maxn][Over];
    int pos[Maxn][Over];
    int tot;
    void add(int num){
    	++tot;
    	for(int i = 0 ; i < Over ; ++i){
    		sum[tot][i] = sum[tot-1][i];
    		pos[tot][i] = pos[tot-1][i];
    	}
    	int now = tot;
    	for(int i = Over - 1 ; i >= 0 ; --i){
    		if(num & (1<<i)){
    			if(!sum[tot][i]){
    				sum[tot][i] = num;
    				pos[tot][i] = now;
    				break;
    			}
    			if(now > pos[tot][i]){
    				swap(now , pos[tot][i]);
    				swap(num , sum[tot][i]);
    			}
    			num ^= sum[tot][i];
    		}
    	}
    }
    int query(int l , int r){
    	int ans = 0;
    	for(int i = Over - 1  ; i >= 0 ; --i){
    		if(sum[r][i] && pos[r][i] >= l){
    			ans = max(ans , ans ^ sum[r][i]);
    		}
    	}
    	return ans;
    }
    
    int main()
    {
    	int T; scanf(" %d",&T);
    	while(T--){
    		lastans = tot = 0;
    		scanf(" %d %d",&N,&Q);
    		for(int i = 1 ; i <= N ; ++i){
    			scanf(" %d",&a[i]);
    			add(a[i]);
    		}
    		while(Q--){
    			int op , l , r;
    			scanf(" %d",&op);
    			if(!op){
    				scanf(" %d %d",&l,&r);
    				l = (l ^ lastans) % N + 1;
    				r = (r ^ lastans) % N + 1;
    				if(l > r)	swap(l , r);
    				lastans = query(l , r);
    				printf("%d\n",lastans);
    			} else {
    				scanf(" %d",&r);
    				add(r ^ lastans);
    				N++;
    			}
    		}
    	}
    }

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值