线性基小结

oiwiki

性质:

  • 线性基内的元素异或起来的到的值就是原集合异或起来得到的值.
  • 线性基是满足性质一的最小的集合.
  • 线性基没有异或和为\(0\)的子集.
  • 线性基中的元素异或出来的值是不一样的.
  • 线性基中每个元素的最高二进制位不同.

构造方法:

  我们设线性基为集合\(A\),\(A_k\)表示最高位为\(k\)的元素值.

  我们每次往线性基内插入一个数\(x\),我们从高位到低位一位一位贪.

  如果\(x\)\(k\)位上是\(1\).
   1. 如果存在\(A_k\),那么我们将\(x \ xor \ A_k\).
   2. 如果不存在\(A_k\),那么我们将\(A_k=x\).

bool insert(LL x) {
	if(!x)return true;
	for(int i=60; ~i; --i) {
		if(x&(1LL<<i)) {
			if(!p[i]) {
				p[i]=x;++cnt;
				return false;	
			}x^=p[i];
			if(!x)return true;
		}
	}
}

: 返回值表示能不能异或出\(0\).

性质四 prove

  假如存在一个集合\(B=[A_i,A_j,A_k]\)异或起来的值等于\(C=[A_x,A_y,A_z]\).令它们两两不同,那么我们可以得到\(A_i\ xor A_j\ xor A_k=A_x\ xor A_y\ xor A_z\).我们令\(A_x\)是这些元素中最大的,显然由于线性基的性质五,不可能有跟它异或起来为\(0\)的值.那么两边注定不能相等.

: 其它几个性质比较好证,这里就不解释了.

补充:

1.选择任意元素异或起来与x的异或最大值

思路:

   我们从最高位往最低位贪心,如果\(Ans\)\(k\)位上为\(0\),那么我们将\(Ans\ xor \ A_k\),否则不进行任何操作.

核心代码:
LL qur_mx(LL x) {//求解选取任意元素异或起来与x的异或最大值.
	LL res=x;
	for(int i=60; ~i; --i)if(!(res&(1LL<<i)))res^=p[i];
	return res;
}

2.查询异或第\(k\)

思路:

  简单来说就是将线性基重建,使得它们之间不会互相干扰,然后我们可以写个类似线段树二分一样的东西.(具体看代码)

核心代码:
    void rebuild() {
		for(int i=60; ~i; --i)
			for(int j=i-1; ~j; --j)
				if(p[i]&(1LL<<j))p[i]^=p[j];
		cnt=0;
		for(int i=0; i<=60; ++i)if(p[i])d[++cnt]=p[i];
	}
	LL kth_Mi(LL k) {
		if(Zero)--k; 
		if(k>=(1LL<<cnt))return -1;
		LL res=0;
		for(int i=cnt; i; --i)
			if(k&(1LL<<i))res^=d[i];
		return res;
	}

3.查询排名

思路:

  大概就是将线性基重建后求解.

核心代码:
    LL rank(LL x) {
		LL rk=1;
		for(int i=cnt; i; --i)
			if(x>=d[i])x^=d[i],rk+=1LL<<i-1;
		return rk;
	}

线性基的应用

P3812 【模板】线性基

思路:板子题.

P3265 [JLOI2015]装备购买

思路:

  数量最多其实就是线性基内的元素个数.

  花费最小,我们可以先按照每个装备的花费排序,从小的选起.装备的属性是一个向量,线性基里的元素其实就是一个向量.我们用类似高斯消元的方法就好了.

P4301 [CQOI2013] 新Nim游戏

思路:

  对于传统的\(nim\)游戏,如果所有元素的\(xor\)和为\(0\),那么先手必败,否则先手必胜.

  再看这道题,当第一个人拿走了若干堆,第二个人的目的就是选出若干个元素使得它们的异或和为\(0\).所以对于第一个人来说,拿掉的应该是不能插入线性基内的元素.我们可以先按数从大到小排序,这样能保证第一回合所取最小.

P4570 [BJWC2011]元素

思路:

  这道题类似上面一题.

P4151 [WC2011]最大XOR和路径

思路:

  我们可以将一条路径拆成两部分,一部分是环,另一部分是随便一条\(1->n\)的链.

  因为我们可以发现对于连接一个环和一条链的边会被遍历两次,对答案没有影响.所以我们可以将每个环丢到线性基里,最后线性基内查询与\(dis[1,n]\ xor\)最大的值.

CF845G Shortest Path Problem?

思路:

  与上一道题一模一样.

CF724G Xor-matic Number of the Graph

思路:

  我们同样将一条路径拆成两部分.

  由于我们直接枚举两个点会超时,考虑计算每一位的贡献.假设当前这位为\(k\).

  如果有一个环的第\(k\)位为\(1\),那么显然我们任意取两个点都存在\(2^{S-1}\)条路径第\(k\)位为\(1\),\(S\)表示线性基的大小.因为我们异或上这个环第\(k\)位上的数就会发生变化.那么\(1\)\(0\)的概率是相等的.答案就是\(2^{S-1}*2^{k}*(sz*(sz-1)/2)\),\(sz\)表示点数.

  如果没有一个环的第\(k\)位为\(1\),那么两个点\(x,y\)它们之间的\(dis=dis[x]\ xor \ dis[y]\),那么我们需要强制其中一个为\(1\),另一个为\(0\).答案就是\(2^k*2^{S}*cnt*(sz-cnt)\).

CF895C Square Subsets

思路:

  我们显然可以将每个二进制位表示每个质数,那么是一个平方数就相当于\(xor\)\(0\).(但是我写了状压dp).

P4869 albus就是要第一个出场

思路:

  首先先说一个结论:如果线性基内的元素有\(S\)个,那么每个能被异或出来的数的异或方案数相同,且为\(2^{n-S}\).

  这个结论想想就是对的,懒得证了.剩下我们只用求一个排名即可.

来个板子(不清楚对不对)

struct XXJ {
	int cnt;
	LL p[100],d[100];
	bool Zero;
	void Clear() {
		Zero=cnt=0;
		for(int i=0; i<60; ++i)d[i]=p[i]=0;	
	}
	bool insert(LL x) {
		if(!x)return true;
		for(int i=60; ~i; --i) {
			if(x&(1LL<<i)) {
				if(!p[i]) {
					p[i]=x;++cnt;
					return false;	
				}x^=p[i];
				if(!x)return true;
			}
		}
	}
	LL qur_mx(LL x) {
		LL res=x;
		for(int i=60; ~i; --i)if(!(res&(1LL<<i)))res^=p[i];
		return res;
	}
	void rebuild() {
		for(int i=60; ~i; --i)
			for(int j=i-1; ~j; --j)
				if(p[i]&(1LL<<j))p[i]^=p[j];
		cnt=0;
		for(int i=0; i<=60; ++i)if(p[i])d[++cnt]=p[i];
	}
	LL kth_Mi(LL k) {
		if(Zero)--k; 
		if(k>=(1LL<<cnt))return -1;
		LL res=0;
		for(int i=cnt; i; --i)
			if(k&(1LL<<i))res^=d[i];
		return res;
	}
	LL kth_Mx(LL k) {
		return kth_Mi((1LL<<cnt)-k+1);
	}
	LL rank(LL x) {
		LL rk=1;
		for(int i=cnt; i; --i)
			if(x>=d[i])x^=d[i],rk+=1LL<<i-1;
		return rk;
	}
}LB;
posted @ 2021-11-18 16:05  syhyyy  阅读(80)  评论(0)    收藏  举报