yuwj  

写在前面

打多校的时候碰到线性基没学过,于是学一下,然后是笔记,与这个题目的AC代码(参考jiangly)

线性基的所有博客和书籍里,我看到的《算法竞赛》(罗勇军)那本才看懂了,水题也切得非常顺快,推荐

其他博客我是啥也没懂

抑或空间线性基

ll P[63];
bool insert(ll x){
    for(int i=62;i>=0;--i){
        if(x >> i & 1){
            if(!P[i]) {P[i]=x;return false;}
            else x ^= P[i];
        }
    }
    return true;
}

ll ask(){
    ll res = 0;
    for(int i=62;i>=0;--i)res = max(res,res^P[i]);
    return res;
}

线性基的概念

思路:将 2^n 种组合空间压缩成 2^m 种组合求抑或

  • 线性基是一个序列,任何一个序列都能生成线性基,定义原序列为A,线性基为P
  • 原序列中的任何一个元素都能通过线性基中任意元素抑或得到

线性基的性质

  • 等价性:在A上进行抑或运算与在P上进行抑或运算结果相同
  • 最小性:P中元素个数最少,任意元素组合满足线性无关性,即不存在P中两个组合抑或结果相等的情况
  • 线性基中不存在抑或和为0的子集

线性基的构造

规则:P中每个元素的二进制位数(位数不是1的个数)不同,P能组合出的抑或和有$2^k - 1$种,其中,k是P的元素个数

  • 所有元素位数不同时,A可以就是P,P也可是其他抑或空间,比如 $A = {1,3,9},P = {1,3,9}、{1,2,9}、{1,2,8}...$
  • 存在相同位数的元素时,P可以通过一个代表元,与其他元素的抑或共同组成,比如$A = {a_1,a_2,a_3},Cnt_{a_1} = Cnt_{a_2} = Cnt_{a_3},则 P = {a_1,{a_1} \oplus {a_2}, {a_1} \oplus {a_3}}$

线性基的应用

  • 搜一些位置,要求剩下位置不相邻就是线性基
  • 最小抑或和,最大抑或和,第k大/小抑或和

最大抑或和

n个数字的任意组合求解抑或最大值

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,x,p[63];
void insert(ll x){
	for(int i=62;i>=0;--i){
		if(x >> i & 1) {
			if(p[i] == 0) {p[i] = x;return;}
			else x ^= p[i];
		}
	}
}
ll ask(){
	ll res = 0;
	for(int i=6 2;i>=0;--i) res = max(res, res ^ p[i]);
	return res;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>x;insert(x);
	}
	cout<<ask()<<'\n';
	return 0;
}

2025杭电多校2 1012

题意:

序列中选择不相邻数字的最大抑或和(n<=50)

思路:

dfs + 线性基

点击查看代码 /* 选/不选dfs生成所有子序列,因为是所有子序列,所以将目前选到的线性基复制一份放到后面去 */

void solve(){
cin>>n;
for(int i=1;i<=n;++i) cin>>num[i];
auto insert = [&](vector &P,ll x){
for(int i=62;i>=0;--i){
if(x >> i & 1){
if(!P[i]) {P[i] = x;return;}
else x^=P[i];
}
}
};

auto ask = [&](vector<ll> P)->ll{
    ll res = 0;
    for(int i=62;i>=0;--i) chkmax(res,res^P[i]);
    return res;
};

ll ans = 0;
auto dfs = [&](auto && self, int i, vector<ll> P)->void{
    if(i >= n+1){
        chkmax(ans,ask(P));
        return;
    }
    //不选a[i],选a[i+1], 下一个选[i+3]
    if(i + 1 <= n){
        auto Q = P;
        insert(Q,num[i+1]); //这里必须选择,然后放到线性基中,是多个子序列,所以是复制一份生成更多的子序列
        self(self,i+3, move(Q));
    }
    //选到了第i个数字,下一个选i+2,线性基(构成的序列是P)
    insert(P, num[i]);
    self(self, i+2, move(P));
};
dfs(dfs,1,vector<ll>(63));
cout<<ans<<'\n';

}


posted on 2025-07-22 11:52  xiaowang524  阅读(33)  评论(0)    收藏  举报