线性基浅谈

蒟蒻的我显然不知道什么是"基",更不知道一大堆概念等乱七八糟的东西,就通俗的讲好了。

OI中线性基的用处 :一个序列a1,a2....an,我们选出任意个数使得答案的异或值最大。

P3812 【模板】线性基

线性基的本质就是找出一些数,使得这些数对答案的贡献与全局的贡献相同(值域相同)。

这道题我们考虑构造法按位贪心。对于每个数,我们从高位往低位扫,若这个数对这一位有贡献(二进制下为1),我们就把这个加入集合,停止扫描(一个数显然不会被选两次啦)。

查询时就从高位到低位扫集合的数组,若使答案变大,则贪心异或。

因为集合中只有log个数,所以时间复杂度为O(nlogn),比高斯消元快许多。

找集合

 

 1     for(intt i=1;i<=n;i++)//扫描每个数字
 2     {
 3         for(intt j=51;j>=0;j--)//按位开桶(集合)
 4         {
 5             if(!(a[i]>>j))//这个数对这一位没有贡献,看下一位
 6                 continue;
 7             if(!p[j])//这一位还没有数
 8             {
 9                 p[j]=a[i];//赋值
10                 break;
11             }
12             a[i]^=p[j];//这里很关键:我们要保证选到的数对集合中的这一位有贡献,贪心才能够成立。
13         }
14     }

 

查询

    for(intt i=51;i>=0;i--)
        if((ans^p[i])>ans)
            ans^=p[i];//贪心

全代码

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define intt long long
#define N 500
intt a[N];
intt p[N];
intt ans=0;
intt n;
int main()
{
    scanf("%lld",&n);
    for(intt i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(intt i=1;i<=n;i++)//扫描每个数字
    {
        for(intt j=51;j>=0;j--)//按位开桶(集合)
        {
            if(!(a[i]>>j))//这个数对这一位没有贡献,看下一位
                continue;
            if(!p[j])//这一位还没有数
            {
                p[j]=a[i];//赋值
                break;
            }
            a[i]^=p[j];//这里很关键:我们要保证选到的数对集合中的这一位有贡献,贪心才能够成立。
        }
    }
    for(intt i=51;i>=0;i--)
        if((ans^p[i])>ans)
            ans^=p[i];//贪心
    printf("%lld",ans);
    return 0;
}

线性基还有许多应用,如果哪里没懂请在讨论中问我,我会解答。

 

posted @ 2018-12-26 19:23  342  阅读(188)  评论(0编辑  收藏  举报