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

线性基在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线性基

原文地址:https://www.cnblogs.com/zimindaada/p/13815396.html