线性基

线性基:

定义:
设数集TT的值域范围为[1,2^n−1][1,2^n−1]。 
TT的线性基是TT的一个子集A={a1,a2,a3,...,an}。 
A中元素互相xor所形成的异或集合,等价于原数集TT的元素互相xor形成的异或集合。 
可以理解为将原数集进行了压缩。即生成了一组线性无关向量组。

 

性质:

1.设线性基的异或集合中不存在00。 
2.线性基的异或集合中每个元素的异或方案唯一,其实这个跟性质1是等价的。 
3.线性基二进制最高位互不相同。 
4.如果线性基是满的,它的异或集合为[1,2^n−1]。 
5.线性基中元素互相异或,异或集合不变。

 

很多情况下,只有有关异或运算和求最值,就可以用到线性基。

模板代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>

using namespace std;

#define FOU(i,x,y) for(int i=x;i<=y;i++)
#define FOD(i,x,y) for(int i=x;i>=y;i--)
#define MEM(a,val) memset(a,val,sizeof(a))
#define PI acos(-1.0)

const double EXP = 1e-9;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const ll MINF = 0x3f3f3f3f3f3f3f3f;
const double DINF = 0xffffffffffff;
const int mod = 1e9+7;
const int N = 1e6+5;

//线性基
struct L_B{
    ll d[63],new_d[63];  //d数组是第一次线性基,new_d是用于求Kth的线性基
    int cnt;             //记录个数
    L_B(){
        memset(d,0,sizeof(d));
        memset(new_d,0,sizeof(new_d));
        cnt=0;
    }
    void clear(){
        memset(d,0,sizeof(d));
        memset(new_d,0,sizeof(new_d));
        cnt=0;
    }
    bool ins(ll val){
        for(int i=62;i>=0;i--){
            if(val&(1ll<<i)){  //存在贡献则继续
                if(!d[i]){     //线性基不存在,选入线性基中
                    d[i]=val;
                    break;
                }
                val^=d[i];     //否则直接改变其值
            }
        }
        return val>0;          //大于0则是成功加入线性基的向量
    }
    ll query_max(){
        ll ans=0;
        for(int i=62;i>=0;i--)
            if((ans^d[i])>ans) //能让值变大则选入
                ans^=d[i];
        return ans;
    }
    ll query_min(){
        for(int i=0;i<=62;i++)
            if(d[i])           //最小异或值
                return d[i];
        return 0;
    }
    //以下代码为求第k大异或值,其中cnt用于判断是否可以取到0
    // cnt==n(数的个数)则不可以取到0,第k小就是第k小,否则第k小是第k-1小
    void rebuild()
    {
        for(int i=62;i>=0;i--)
            for(int j=i-1;j>=0;j--)
                if (d[i]&(1LL<<j))
                    d[i]^=d[j];
        for (int i=0;i<=62;i++)
            if (d[i])
                new_d[cnt++]=d[i];
    }
    ll kthquery(int k)
    {
        ll ans=0;
        if (k>=(1ll<<cnt))
            return -1;
        for (int i=62;i>=0;i--)
            if (k&(1ll<<i))
                ans^=new_d[i];
        return ans;
    }
};

//线性基合并,暴力合并
L_B merge(const L_B &n1,const L_B &n2)
{
    L_B ret=n1;
    for (int i=62;i>=0;i--)
        if (n2.d[i])
            ret.ins(n2.d[i]);
    return ret;
}

int main()
{
    //freopen("xor_equ.in","r",stdin);
    //freopen("xor_equ.out","w",stdout);
    std::ios::sync_with_stdio(false);
    int n;
    while(~scanf("%d",&n)){
        L_B lb;
        for(int i=1;i<=n;i++){
            ll x;
            scanf("%lld",&x);
            lb.ins(x);
        }
        printf("%lld\n",lb.query_max());
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值