• [模板] 线性基


    简介

    线性基是一个最小的集合 (S = {p_i}, i in { 0, 1, cdots ,n-1 }), (p_i) 为非 (0) 二进制数, 且每个数在异或意义下线性无关. 将 (n) 称作线性基的.

    它可以通过异或唯一的表示出 (T = {a_i}, i in { 0, 1, cdots ,m-1 }) 中的每一个值. 利用线性代数的知识可以知道 (m = 2^n).
    也就是说, (S)(T) 在异或意义下张成的空间等价.

    为了便于维护和查询, 规定 (p_i in S) 的二进制最高位为第 (i) 位, 值为1.

    构造

    根据定义构造即可. rk 表示线性基的秩, 即 (S) 集合的大小; zerofl 表示给定的数能否表示出 (0).

    时间复杂度 (O(log v)), 其中(v)为值域大小.

    const int l2sz=70,lbnd=52;
    ll lbase[l2sz],rk=0;
    void insert(ll v){
    	repdo(i,lbnd,0){
    		if(((v>>i)&1)==0)continue;
    		if(lbase[i])v^=lbase[i];
    		else{lbase[i]=v,++rk;return;}
    	}
    	zerofl=1;
    }
    

    应用

    能否表示

    类似构造, 如果某次异或后为0则能表示; 否则不能.

    求最大异或值

    对于每一位, 如果异或之后变大就异或.

    正确性显然.

    ll qmax(){
    	ll res=0;
    	repdo(i,lbnd,0)if((res^lbase[i])>res)res=res^lbase[i];
    	return res;
    }
    

    最小异或值

    如果能表示出0则为0; 否则, 显然数组中最小非0值为最小异或值.

    ll qmin(){
    	if(zerofl)return 0;
    	rep(i,0,lbnd)if(lbase[i])return lbase[i];
    	return -1;
    }
    

    简化线性基

    类似高斯消元, 容易发现可以使线性基在每一位最多只有一个 1, 也就是说, 如果有这样的一个线性基

    [A = egin{bmatrix} 1 & 0 & 1\ 0 & 0 & 0\ 0 & 0 & 1\ end{bmatrix} ]

    它可以被简化为

    [A = egin{bmatrix} 1 & 0 & 0\ 0 & 0 & 0\ 0 & 0 & 1\ end{bmatrix} ]

    从而使得每一列至多有一个 (1).

    实现上, 只需将每一行用前面的行异或消去即可.

    void reduce(){
    	rep(i,0,lbnd){
    		rep(j,0,i-1){
    			if((lbase[i]>>j)&1)lbase[i]^=lbase[j]; 
    		}
    		if(lbase[i])rbase[++pr]=lbase[i];
    	}
    }
    

    第k大异或值

    首先简化线性基.

    现在, 对于每一个非空的行 (i), 这一行的值 (p_i) 都是 (i) 位为 (1) 的最小的值.

    由于这些行可以任意组合, 对于 (k) 的每一个为 (1) 的位 (i), 把答案异或上从小到大第 (i) 个非零的行即可.

    ll qkmin(ll k){
    	k-=zerofl;
    	if(k==0)return 0;
    	if(k>=(1ll<<(pr+1)))return -1;
    	ll res=0;
    	rep(i,0,pr){
    		if((k>>i)&1)res^=rbase[i];
    	}
    	return res;
    }
    

    表示出k的方案数

    首先, 如果线性基不能表示出 (k), 那么答案为 (0);

    否则, 考虑没有加入线性基的数的个数 (n') (即 (总数字个数 - rank)). 任意选择这 (n') 个数, 并加入和选择的数异或和为 (0) 的数, 总方案数即为 (2^{n'}).

    (0) 是一个特例. 由于线性基中的数不能表示出 (0), 方案数为 (2^{n'} - 1).

    所有能表示出的数之和

    所有能表示出的数的个数为 (2^n - 1). 如果可以表示出 (0) (zerofl == 1), 则为 (2^n).

    显然每一位可以分开考虑.
    对于从小到大第 (i) 位 (从 (0) 开始), 它对答案的贡献为 (2^i cdot cnt), 其中 (cnt) 为这一位为 (1) 的数的出现次数.

    考虑如何求 (cnt).
    对于第 (i) 位, 如果线性基上的 (n) 个数第 (i) 为均为 (0), 那么能表示出的数的这一位都必为 (0), 则 (cnt)(0);
    否则, 这一位可以为 (1), 而其他位可以由线性基上的数任意组合, 共 (2^{n-1}) 种, 即 (cnt = 2^{n-1}).

    代码:

    ll getsum(){
    	ll ans=0;
    	rep(i,0,62){
    		int fl=0;
    		rep(j,0,62)fl|=((lbase[j]>>(ll)i)&1);
    		if(fl)ans+=(1ll<<(rk-1))*(1ll<<i);
    	}
    	return ans;
    }
    

    总的代码

    loj114: k大异或和

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<map>
    #include<set>
    using namespace std;
    #define rep(i,l,r) for(register int i=(l);i<=(r);++i)
    #define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
    #define il inline
    typedef long long ll;
    typedef double db;
    
    //---------------------------------------
    const int lsz=70,lbnd=52;
    int n,m;
    
    ll lbase[lsz],rk=0,zerofl=0;
    ll rbase[lsz],pr=-1;//real base
    void insert(ll v){
    	repdo(i,lbnd,0){
    		if(((v>>i)&1)==0)continue;
    		if(lbase[i])v^=lbase[i];
    		else{lbase[i]=v,++rk;return;}
    	}
    	zerofl=1;
    }
    ll qmax(){
    	ll res=0;
    	repdo(i,lbnd,0)if((res^lbase[i])>res)res=res^lbase[i];
    	return res;
    }
    ll qmin(){
    	if(zerofl)return 0;
    	rep(i,0,lbnd)if(lbase[i])return lbase[i];
    	return -1;
    }
    void reduce(){
    	rep(i,0,lbnd){
    		rep(j,0,i-1){
    			if((lbase[i]>>j)&1)lbase[i]^=lbase[j]; 
    		}
    		if(lbase[i])rbase[++pr]=lbase[i];
    	}
    }
    ll qkmin(ll k){
    	k-=zerofl;
    	if(k==0)return 0;
    	if(k>=(1ll<<(pr+1)))return -1;
    	ll res=0;
    	rep(i,0,pr){
    		if((k>>i)&1)res^=rbase[i];
    	}
    	return res;
    }
    
    int main(){
    	ios::sync_with_stdio(0),cin.tie(0);
    	ll a;
    	cin>>n;
    	rep(i,1,n)cin>>a,insert(a);
    	reduce();
    	cin>>m;
    	rep(i,1,m)cin>>a,cout<<qkmin(a)<<'
    ';
    	return 0;
    }
    
  • 相关阅读:
    解决ios下iframe不能滑动
    每天一题之js执行顺序
    async函数的返回值
    小程序自定义Tabbar
    windows10配置vue3项目踩坑记录
    vue2+循环链表解决一个历史趣题
    小程序内协议使用的三种方法
    HDFS的java操作
    HDFS工作原理笔记
    win10已经编译好的hadoop2.6.5
  • 原文地址:https://www.cnblogs.com/ubospica/p/10391964.html
Copyright © 2020-2023  润新知