• 笔记 线性基


    定义

    在线性代数中,基又称为基地,是刻画向量的工具。对于基底的元素我们称为基向量,向量空间的任意一个元素都可以唯一表示成为基向量的线性组合。同样,线性基也是一种基,它是一种特殊的基,一般用来求异或问题

    我不知道你看懂没有,反正我是没看太懂,只需要知道线性基处理异或问题时十分方便就好。

    性质

    • 将线性基中的一些元素异或一定可以得到完整的原数列
    • 原数列中异或的值域与线性基异或的值域相同
    • 线性基中没有异或和为0的子集
    • 线性基中选取元素的每种方案异或得到的数一定不相同
    • 对于一个数列来说,线性基中元素的个数是唯一的,并且是满足以上性质的最小集合

    简单证明一下(为了防止有理解问题,用xor表示异或运算):
    对于性质一,构建线性基时可以维护,下边再提。
    对于性质二,可以由性质一推得。
    对于性质三,如果有(x_1 xor x_2 ………… xor x_n = 0),那么一定有(x_1 xor x_2 …… xor x_n = x_i),就是说(x_i)可以由已经有的元素异或得到,根据性质一可以知道,原数列的每一个数都能由线性基中的数异或得到,换言之,(x_i)没有必要加入线性基,所以性质三得证。
    对于性质四,和性质三的推导大致相同,懒得写了。
    对于性质五,综上可得。

    基本操作

    1. 插入
      从高到低依次考虑该数字二进制的每一位,若该位是1,那么找到该位对应的线性基,如果是空的,直接替换掉并且退出插入,如果不是空的,那么用该数字异或线性基上的数,然后继续插入,直到找到一个空的线性基或者该数被异或为0,若找到一个空的线性基,则说明插入成功,若被异或成0,说明插入失败,这时可以知道该数字能被线性基中的一个子集异或得到,没有必要插入线性基。
      要注意的是左移时考虑左移的是1还是1ll这里很容易挂。
    bool Insert(ll w){
    	for(int i=62;i>=0;i--){
    		if(w&(1ll<<i)){
    			if(!bas[i]){
    				bas[i]=w;
    				return 1;
    			}
    			w^=bas[i];
    		}
    	}
    	return 0;
    }
    

    2.查询数列异或和最小值
    由线性基的性质可以知道,线性基中的最小的位上的不为0的数即答案,注意判断0的情况。
    3.查询数列异或和最大值
    根据插入的函数,第i个线性基中的数的最高位上的1是第i位,而高位上放1肯定比放0优,所以尽量让每个高位上都是1。

    for(int i=55;~i;i--)
    		if((ans^p[i])>ans)
    			ans^=p[i];
    

    或者

    for(int i=62;i>=0;i--)
    		if(b[i]&&((ans&(1ll<<i))==0))
    			ans^=b[i];
    

    4.查询第k小值,暂时不会。

    例题

    1.模板
    这个就不说了,挺模板的。
    2.元素
    题意是让我们从一个序列中选出它的一个子集,使得选出来的数异或和不能得到0,且得到的数权值和最大。
    异或和不能得到0可以构建一个线性基维护
    线性基有一个很好的性质,就是插入顺序对线性基没有影响,可以参照上述性质五,既然这样,加一个大的也是加,加一个小的也是加,那肯定要加入大的啊,所以排一遍序一次考虑能不能加入线性基即可。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int lqs=1e3+10;
    struct Node{
    	ll idx;
    	int val;
    	bool operator<(const Node&A)const{
    		return val>A.val;
    	}
    }p[lqs];
    ll bas[70];
    bool Ins(ll w){
    	for(int i=62;i>=0;i--){
    		if(w&(1ll<<i)){
    			if(!bas[i]){
    				bas[i]=w;
    				return 1;
    			}
    			w^=bas[i];
    		}
    	}
    	return 0;
    }
    int main(){
    	int n;
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%lld%d",&p[i].idx,&p[i].val);
    	}
    	sort(p+1,p+n+1);
    	int ans=0;
    	for(int i=1;i<=n;i++){
    		if(Ins(p[i].idx))
    			ans+=p[i].val;
    	}
    	printf("%d
    ",ans);
    }
    

    3.彩灯
    这个会发现就是问这个序列的子集异或能得到多少不同的数,然后这几个字符串长度都不超过50,可以转换成十进制数。
    将这几个十位数都丢到线性基里边然后线性基的集合个数就是答案,注意一个灯都不开也是一种情况,依据是上述性质一。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int lqs=60;
    ll p[lqs],col[lqs];
    char s[lqs];
    bool Ins(ll w){
    	for(int i=59;i>=0;i--){
    		if(w&(1ll<<i)){
    			if(!p[i]){
    				p[i]=w;
    				return 1;
    			}w^=p[i];
    		}
    	}
    	return 0;
    }
    int pow(int cnt,int w,int mod){
    	int ans=1;
    	while(cnt){
    		if(cnt&1)ans=1ll*ans*w%mod;
    		w=1ll*w*w%mod;
    		cnt>>=1;
    	}
    	return ans;
    }
    int main(){
    	int n,m;
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		scanf("%s",s);
    		for(int j=0;j<n;j++)
    			if(s[j]=='O')col[i]|=1ll<<j;
    	}
    	int ans=0;
    	for(int i=1;i<=m;i++)
    		if(Ins(col[i]))ans++;
    	printf("%d
    ",pow(ans,2,2008));
    }
    
  • 相关阅读:
    分享一些书籍,方方面面,很多值得一读
    C#网络爬虫--多线程处理强化版
    图书管理系统
    jquery完成界面无刷新加载登陆注册
    springboot jar项目 改为war项目
    nginx 配置文件配置(ssl和代理80端口)
    linux 安装mysql8.0
    linux redis安装和启动,远程链接
    linux nginx 安装启动
    linux tar方式安装配置jdk
  • 原文地址:https://www.cnblogs.com/anyixing-fly/p/13200065.html
Copyright © 2020-2023  润新知