zoukankan      html  css  js  c++  java
  • [笔记]线性基

    比较好懂的博客

    里面有比较多的题型还有证明

    注意

    • long long
    • 成功插入线性基之后记得返回

    线性基用来干什么

    维护一个序列的异或值:

    (d[])为序列(a[])的线性基,那么:

    1. (d[])中任意一些元素异或的集合与(a[])中任意一些元素异或的集合相同
    2. (d[])中任意一些元素异或不为0
    3. (d[])是所有满足条件1的集合中最小的

    其中(d[i])的第(i)位上为1

    插入

    从高位往低位找

    • 如果当前为没有值,直接插入并返回
    • 否则,把(x)异或上(d[i]),继续找
    int INS(LL x){
    	tep(i,60,0){
    		if(!((x>>i)&1))continue;
    		if(!d[i]){
    			d[i]=x;
    			cnt++;//记录线性基中有多少元素
    			return 1;
    		}
    		x^=d[i];
    	}
    	return 0;//返回值表示是否能够成功插入(如果原来的线性基中已经存在某一些
    			 //数异或和为X,那么x就没有必要被插入。这个时候a[]异或可以得到0,需要特判
    }
    

    查询

    最大值

    从高位往低位扫,如果(ansoplus d[i]>ans),那么把(ans)异或上(d[i]),否则说明(ans)的第(i)为已经有(1),异或(d[i])只会使答案变小,就skip

    最小值

    就是(d[])里面最小的

    (k)

    就是求所有能异或得到的数中第(k)小的那个

    传送门


    Part1

    先重构(d[]),使得对于每一个(i),最多只有一个(d[j])的第(i)位上为1

    rep(i,1,maxn)rep(j,0,i-1){
    		if((d[i]>>j)&1)d[i]^=d[j];
    }
    

    正确性:

    (d[i]oplus d[j])后,线性基里的元素从(d[i],d[j])变成了(d[i]oplus d[j],d[j]),可以发现,这两个元素异或得到的集合还是不变的,所以整个集合的异或值也不会变


    Part2

    先上代码

    rep(i,0,60){
    		if(d[i]!=0){
    			if(k&1)ans^=d[i];
    			k/=2;
    		}
    	}
    

    没想到吧,二进制真是奇妙,自己琢磨吧


    Part3 Notice

    注意判断0。如果原序列可以异或得到0,那么k要减1

    猴啦上代码啦

    Code

    #include<bits/stdc++.h>
    #define rep(X,A,B) for(int X=A;X<=B;X++)
    #define tep(X,A,B) for(int X=A;X>=B;X--)
    #define LL long long
    const int N=1000010;
    const int M=2000010;
    const int maxn=60;
    using namespace std;
    
    void read(int &x){
    	scanf("%d",&x);
    }
    
    int n,Q,flg,cnt=0;
    LL d[N],tot;
    
    int INS(LL x){
    	tep(i,60,0){
    		if(!((x>>i)&1))continue;
    		if(!d[i]){
    			d[i]=x;
    			cnt++;
    			return 1;
    		}
    		x^=d[i];
    	}
    	return 0;
    }
    
    void READ(){
    	LL x;
    	flg=1;
    	cnt=0;
    	read(n);
    	rep(i,0,60)d[i]=0;
    	
    	rep(i,1,n)scanf("%lld",&x),flg&=INS(x);
    	rep(i,1,maxn)rep(j,0,i-1){
    		if((d[i]>>j)&1)d[i]^=d[j];
    	}
    	tot=1;
    	rep(i,1,cnt)tot*=2;tot--;
    	read(Q);
    }
    
    LL SOLVE(){
    	LL k,ans=0;
    	scanf("%lld",&k);
    	if(!flg)k--;
    	if(k>tot)return -1;
    	rep(i,0,60){
    		if(d[i]!=0){
    			if(k&1)ans^=d[i];
    			k/=2;
    		}
    	}
    	return ans;
    }
    
    int main(){
    	int T;
    	read(T);
    	rep(op,1,T){
    		printf("Case #%d:
    ",op);
    		READ();
    		while(Q--)printf("%lld
    ",SOLVE());
    	}
    	return 0;
    }
    
  • 相关阅读:
    349、两个数组的交集 | JS集合
    JS集合Set
    JS里的队列和链表
    使用链表指针获取JSON的节点值
    141、环形链表 | JS-链表
    83、删除排序链表中的重复元素 | JS-链表
    2、两数相加 | JS-链表
    事件循环与任务队列
    933、最近的请求次数 | JS-队列
    栈JS实现
  • 原文地址:https://www.cnblogs.com/SCL123/p/11707688.html
Copyright © 2011-2022 走看看