P3812 【模板】线性基

不得不说,线性基是一个神奇的东西

它维护的东西与“异或”密切相关

 

题意:

给定n个整数(数字可能重复),求在这些数中选取任意个,使得他们的异或和最大。

当然,也可以最小,甚至,可以求任意异或和第k大!(哇,好niubi)

 

做法:开一个数组a[MAXN],MAXN是数字最高位数。

  a[i]表示当前线性基内任意异或出来的数字中,最高位为i的任意一个数字。

 

插入x|判断x是否存在

从高位到低位枚举所有位数,如果x的第i位有值:如果a[i]不存在,则a[i]=x,并退出。

             如果a[i]存在,令x^=a[i]。如果最后x变成了0,那么说明x在线性基内。

inline bool insert(int x)
{
    for(int i=nmr;i>=0;i--)
    {
        if(x&(1LL<<i))
        {
            if(a[i]==0)
            {
                a[i]=x;
                break;
            }
            x^=a[i];
        }
    }
    return x>0;
}

查询最大值

从高位到低位扫描线性基。如果异或之后答案变大,就把这一位异或到答案。

inline int get_max()
{
    int ans=0;
    for(int i=nmr;i>=0;i--)
    { 
        if((ans^a[i])>ans)
            ans^=a[i];
    }
    return ans;
}

 

查询最小值

从低位到高位扫描线性基。最低位上的线性基即为答案。

inline int get_min()
{
    for(int i=0;i<=nmr;i++)
        if(a[i]>0) return a[i];
}

第k小

首先我们要改造一下线性基。我们把线性基改造成每一位相互独立,意思就是对于第i位,

线性基上只有第i位可能是1。(其它位是1就不相互独立了(互相异或不对了))具体如何改造,就是从高位向低位扫描,

对于第i位线性基a[i],对于j<i,如果a[i]的第j位是1,就让a[j]异或上a[i]。

查询的时候,将k进行二进制拆分,对于的1位,就异或对应的线性基。

最终得到的答案是第k小值。

inline void rebuild()
{
    cnt=0;
    for(int i=nmr;i>=0;i--)
    {
        for(int j=i-1;j>=0;j--)
            if(a[i]&(1LL<<j))
                a[i]^=a[j];
    }
    for(int i=0;i<=nmr;i++)
        if(a[i])
            p[cnt++]=a[i];
}
inline int query(int k)
{
    int ans=0;
    if(k>=(1LL<<cnt)) return -1;
    for(int i=nmr;i>=0;i--)
    {
        if(k&(1LL<<i))
            ans^=p[i];
    }
    return ans;
}

so

线性基码量小,而且速度快(遇到异或问题可以想到线性基666)

 

基于本题:只要查询最大值即可,这里给出全代码

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define olinr return
#define nmr 60
#define love_nmr 0
#define int long long
int a[nmr];
int cnt;
int n;
int x;
int p[nmr];
inline bool insert(int x)
{
    for(int i=nmr;i>=0;i--)
    {
        if(x&(1LL<<i))
        {
            if(a[i]==0)
            {
                a[i]=x;
                break;
            }
            x^=a[i];
        }
    }
    return x>0;
}
inline int get_max()
{
    int ans=0;
    for(int i=nmr;i>=0;i--)
    { 
        if((ans^a[i])>ans)
            ans^=a[i];
    }
    return ans;
}
inline int get_min()
{
    for(int i=0;i<=nmr;i++)
        if(a[i]>0) return a[i];
}
inline void rebuild()
{
    cnt=0;
    for(int i=nmr;i>=0;i--)
    {
        for(int j=i-1;j>=0;j--)
            if(a[i]&(1LL<<j))
                a[i]^=a[j];
    }
    for(int i=0;i<=nmr;i++)
        if(a[i])
            p[cnt++]=a[i];
}
inline int query(int k)
{
    int ans=0;
    if(k>=(1LL<<cnt)) return -1;
    for(int i=nmr;i>=0;i--)
    {
        if(k&(1LL<<i))
            ans^=p[i];
    }
    return ans;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>x;
        insert(x);
    }
    cout<<get_max();
    olinr love_nmr;
}

 

posted @ 2018-08-14 20:48  olinr  阅读(257)  评论(0编辑  收藏  举报