线性基浅谈

Posted h-lka

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性基浅谈相关的知识,希望对你有一定的参考价值。

线性基通常用于解决一系列关于异或的问题。

定义:线性基支持log插入,用一个数组来存储线性基,其中,第i个线性基就是第i位是1的数。

由此,给出插入线性基的板子。

void insert(int x)
        for(int i=31;i>=0;i--)
            if(!(x&(1<<i)))continue;
            if(!p[i])
                p[i]=x;
                break;
            x^=p[i];
        
    

其中,(x&(1<<i))是用来取出x的第i位的。

如果是0,则跳出,匹配失败。如果是1,则继续匹配。

记住要有break。

由此,可以得出线性基的三个性质:

1.原序列中的任意数都可以由线性基中的数异或得到。

2.线性基中的数异或起来不能为0.

3.线性基中的数在性质1的条件下,数量是唯一的且最少的。

证明性质1:

当插入失败时,则有x^a[1]^a[2]...=0,即a[1]^a[2]^...=x.

所以,线性基序列中一定可以通过异或得到原序列中的x.

当插入成功时,可得知,x在前面一定异或了一些数,即:

x^a[1]^a[2]....=a[i].

所以,a[1]^a[2]^...^a[i]=x.

综上所述,性质1得证。

证明性质2:

设p[1]^p[2]^p[3]=0,(下标是加入顺序)

则有p[1]^p[2]=p[3],即p[3]不可能被加入线性基。

综上所述,性质2得证。

证明性质3:

首先,如果序列中所有元素都可以被加入线性基,显然性质3成立。

当有些数(x)不可以加入时,则有:

设p[1]^p[2]^p[3]=x,

则有p[1]^p[2]^x=p[3].

也就是说,当你改变插入顺序,只会改变插入的数,数量不会改变。

综上所述,性质3得证。

例1:模板题(点击标题即可进入题目)

题目大意不多说。正常插入,对于每一个数,求出它与其它数的最大异或值即可。

当然要用线性基啦。

代码:

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
ll p[101],a[51];
ll ans,n;
inline void read(ll &x)
    ll s=0,w=1;
    char ch=getchar();
    while(ch<0||ch>9)
        if(ch==-)w=-1;
        ch=getchar();
    while(ch>=0&&ch<=9)
        s=(s<<1)+(s<<3)+(ch^48);
        ch=getchar();
    x=s*w;
inline void get(ll x)
    for(int i=62;i>=0;--i)
        if(!(x>>(ll)i))continue;
        if(!p[i])
            p[i]=x;
            break;
        
        x^=p[i];
    
int main()
    read(n);
    for(ll i=1;i<=n;++i)read(a[i]);get(a[i]);
    for(int i=62;i>=0;i--)
        if((ans^p[i])>ans)
            ans^=p[i];
    printf("%lld\n",ans);
    return 0;

 

注意最后的ans,依次比对,记住加括号。

 

例题2:元素(点击标题即可进入题目)

题意就是,每个元素有两个值,一个标号,一个贡献,当且仅当前面选中的物品的标号无法通过异或得到当前物品标号时,当前物品才能选。

那么,考虑贪心

对于初始序列,来一发sort快排,以价值为关键字。之后,遍历序列,插入每个数的标号判断是否合法,可以在插入时判断。插入后,若合法,则记录贡献,否则pass.

本题考虑性质3的理解。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm> 
using namespace std;
typedef long long ll;
ll p[101];
ll ans,n;
inline void read(ll &x)
    ll s=0,w=1;
    char ch=getchar();
    while(ch<0||ch>9)
        if(ch==-)w=-1;
        ch=getchar();
    while(ch>=0&&ch<=9)
        s=(s<<1)+(s<<3)+(ch^48);
        ch=getchar();
    x=s*w;
inline bool get(ll x)
    for(int i=62;i>=0;--i)
        if(!(x>>(ll)i))continue;
        if(!p[i])
            p[i]=x;
            return true;
        
        x^=p[i];
    return false;

struct node
    ll x,y;
a[500000];
bool cmp(node q,node w)
    return q.y>w.y;
 
int main()
    read(n);
    for(ll i=1;i<=n;++i)read(a[i].x);read(a[i].y);
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)
        if(get(a[i].x))ans+=a[i].y;
     printf("%d\n",ans);
    return 0;

例3:P哥的桶(点击标题即可进入题目)

题目中要求做到区间查询,单点插入(修改),异或和最大。

考虑线段树维护线性基。

对于每一个节点都维护自己区间的线性基。对于插入,线性基插入即可。

对于查询,我们随时开一个外部结构体(同样类型),每次把合法区间的线性基合并,最后在这里查询即可。

(我还是太菜了,涨知识了qwq)

代码:

 

#include<cstdio>
#include<iostream>
#define MAXN 50000
#include<cstring>
using namespace std;
int n,m,opt,k,x;
struct node
    int p[50];
    void insert(int x)
        for(int i=31;i>=0;i--)
            if(!(x&(1<<i)))continue;
            if(!p[i])
                p[i]=x;
                break;
            x^=p[i];
        
    
    void Ins(node &n)
        for(int i=31;i>=0;i--)
        if(n.p[i])insert(n.p[i]);
    
tr[MAXN<<2],ans;
inline void Insert(int cur,int l,int r,int k,int x)
    tr[cur].insert(x);
    if(l==r)return;
    int mid=(l+r)>>1;
    if(k<=mid)Insert(cur<<1,l,mid,k,x);
    else Insert(cur<<1|1,mid+1,r,k,x);

inline void query(int x,int l,int r,int ql,int qr)
    if(ql<=l&&qr>=r)ans.Ins(tr[x]);return;
    int mid=(l+r)>>1;
    if(qr<=mid)query(x<<1,l,mid,ql,qr);
    else if(mid<ql)query(x<<1|1,mid+1,r,ql,qr);
    else query(x<<1,l,mid,ql,mid),query(x<<1|1,mid+1,r,mid+1,qr);

int main()
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        scanf("%d%d%d",&opt,&k,&x);
        if(opt==1)
            Insert(1,1,m,k,x);
        else
            memset(ans.p,0,sizeof(ans.p));
            query(1,1,m,k,x);
            int maxn=0;
            for(int i=31;i>=0;i--)
                if((maxn^ans.p[i])>maxn)
                    maxn^=ans.p[i];
            printf("%d\n",maxn);
        
    
    return 0;
 

 

以上是关于线性基浅谈的主要内容,如果未能解决你的问题,请参考以下文章

浅谈线性素数筛

BZOJ2720浅谈期望线性性分部转移

如何在android中的地图片段内中心线性布局?

浅谈线性表的基本操作与应用

浅谈线性基

浅谈Java数据结构和算法