• 线性基学习笔记(待填坑)


    线性基在OI里是一种用来维护数列异或信息的一种数据结构。本质上他是线性代数的一些东西,反正我是完全不会的。

    构造

    维护一个数组LB[log maxn](Linear Basis,即线性基)。每次向线性基数组内插入一个新的数(x)的时候,设(x)的每一二进制位可以被表示为(x_i),最高位为(x_k),如果LB[k]没有存过别的数,则令LB[k] = x,否则,则让(x)异或上LB[k],再往下找到新的(x)的最高位,如上做若干遍,直到要么(x)存进了某一个LB[p],要么遍历完了所有的最高位还没存进去,那就不用存了,复杂度易证明为(O(log 值域))

    判断某个数是否存在于线性基或能被线性基里面的数表示的时候,就是把上面的存数的方法反过来,每次判断(x)的二进制下最高位,如果LB[k]不存在,则该数无法被表示,否则将(x)异或上LB[k],然后再做一遍。如上做若干遍,直到发现了一个(x)的最高位(k),而LB[k]里面没有值,或者遍历完了所有的(x)的最高位,那么说明可以被异或表示。

    在查询整个数列的最大异或和的时候,从LB[log maxn]开始枚举,如果ans ^ LB[i] > ansans ^= LB[i],否则ans不变。

    最小异或值的话就简单很多。如果出现过某一个数被遍历完了没存进去,就说明这些数可以被组成线性基的这些数组成。(没存进去就是因为数被线性基里面的数层层异或完了,而线性基的数又可以用原数异或表达出来),而如果没有没存进去的,那么就是当前所出现的最小的数(因为每一次跳转操作,(x)的二进制最高位必定单调降低,所以不可能插入两个二进制最高位相同的数。而此时,线性基中最小值异或上其他数,必定会增大,所以它确实是异或最小值)。

    感性证明

    啊这……这谁会写啊,前面构造的时候一些怪异的东西写了一下不严谨证明,其他看起来都是极其显然正确的罢……

    线性代数证明(待填坑)

    实数线性基(待填坑)

    例题:P3812 【模板】线性基

    十分的裸……

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    namespace ztd{
        using namespace std;
        typedef long long ll;
        template<typename T> inline T read(T& t) {//fast read
            t=0;short f=1;char ch=getchar();double d = 0.1;
            while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
            while (ch>='0'&&ch<='9') t=t*10+ch-'0',ch=getchar();
            if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
            t*=f;
            return t;
        }
    }
    using namespace ztd;
    const int maxn = 55;
    ll n, m, a[maxn], ans;
    ll xxj[70];
    signed main(){
        read(n);
        for(int i = 1; i <= n; ++i){
        	read(a[i]);
        	for(int j = 51; j >= 0; --j){
        		if(a[i] >= (1ll << j)){
        			if(xxj[j]) a[i] ^= xxj[j];
        			else{
        				xxj[j] = a[i];
        				break;
        			}
        		}
        	}
        } 
        ll ans = 0;
        for(int i = 51; i >= 0; --i){
        	if((ans ^ xxj[i]) > ans){
        		ans ^= xxj[i];
        	}
        }
        cout << ans << '
    ';
        return 0;
    }
    

    例题2:P4151 【WC2011】最大XOR和路径

    可以很方便的用线性基和异或的性质维护。随便先找出一个点,从他开始dfs路径。路径长度无所谓。由于原题目中说了可以多次走同一个边,并且再加上异或的 x^x = 0这个优秀的性质,我们可以从dfs到的每一个点向外走判环,把环的贡献加上来。至于为什么这样是最优的,因为我们一条路径加上一些环,可以被看作另外一条路径加上另外一些环,总和不变。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    namespace ztd{
        using namespace std;
        typedef long long ll;
        template<typename T> inline T read(T& t) {//fast read
            t=0;short f=1;char ch=getchar();double d = 0.1;
            while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
            while (ch>='0'&&ch<='9') t=t*10+ch-'0',ch=getchar();
            if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
            t*=f;
            return t;
        }
    }
    using namespace ztd;
    #define int long long  
    const int maxn = 50005;
    const int maxm = 1e5+7;
    ll n, m;
    
    struct edge{ int y, gg; ll w; }e[maxm<<1];
    int last[maxn], ecnt;
    inline void addedge(int x, int y, ll w){
    	e[++ecnt] = {y,last[x],w};
    	last[x] = ecnt;
    }
    
    class LinearBasis{
    	private:
    	ll xxj[70];
    	public:
    	inline void add( int x){
    		for(int j = 62; j >= 0; --j){
    	    	if(x >= (1ll << j)){
    	    		if(xxj[j]) x ^= xxj[j];
    	    		else{
     		   			xxj[j] = x;
    	 	   			return;
        			}
        		}
        	}
    	}
    	inline ll query(int x){
    		ll ans = x;
    		for(int i = 62; i >= 0; --i){
      	  		if((ans ^ xxj[i]) > ans){
      	  			ans ^= xxj[i];
        		}
        	}
        	return ans;
    	}
    }LB;
    ll vis[maxn], back[maxn];
    void dfs(int x, ll val){
    	back[x] = val, vis[x] = 1;
    	for(int i = last[x], y = e[i].y; i; i = e[i].gg, y = e[i].y){
    		if(!vis[y]) dfs(y, val^e[i].w);
    		else LB.add(val^e[i].w^back[y]);
    	}
    }
    signed main(){
        read(n); read(m); ll xx, yy, ww;
        for(int i = 1; i <= m; ++i){
        	read(xx); read(yy); read(ww);
        	addedge(xx,yy,ww); addedge(yy,xx,ww);
        } 
    	dfs(1,0);
    	cout << (LB.query(back[n])) << '
    ';
        return 0;
    }
    

    最后再附一个模板链接:ZTL线性基

  • 相关阅读:
    【开发者成长】喧哗的背后:Serverless 的挑战
    都在说实时数据架构,你了解多少?
    谊品生鲜:放弃传统数据库架构,全站上阿里云
    纯干货 | 一篇讲透如何理解数据库并发控制
    作为后端开发如何设计数据库系列文章(二)设计大数据量表结构
    如果千百年前有视觉AI算法,世界将会是什么样的光景呢?
    淘宝万亿级海量交易订单存储在哪?
    跬步千里 —— 阿里云Redis bitfield命令加速记
    容器环境自建数据库、中间件一键接入阿里云 Prometheus 监控
    常用Maven插件介绍(转载)
  • 原文地址:https://www.cnblogs.com/zimindaada/p/13815396.html
Copyright © 2020-2023  润新知