zoukankan      html  css  js  c++  java
  • 线性基讲解

    首先,什么是线性基:

    线性基是一个数的集合,任意一个序列都有至少提个线性基。
    有一组数a1,a2...an和线性基d1,d2...,dm,di表示**最高位1在第i位的数**。
    线性基的作用
    由于线性基值域与原数列值域相同的特点,可以用它来维护异或和。
    线性基的性质

    线性基有以下三大性质:

    1.原序列里面的任意一个数都可以由线性基里面的一些数异或得到。
    2.线性基里面的任意一些数异或起来都不能得到0。
    3.线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的。


    线性基的插入代码

    bool add(ll x)
    {
        for(int i=30;i>=0;i--)
        {
            if((x>>i)&1)
            {
                if(d[i]) x^=d[i];
                else
                {
                    d[i]=x;
                    return 1;
                }
            }
        }
        return 0;
    }

    这样这个d数组的性质就是若d[i]不为0,则(d[i])2的第d[i]的第i+1位为1,并且没有更高位为1了。

    性质1的证明

    如果有了前面讲的,设原数列有一个数x,我们尝试用它来构造线性基,这样就会有两种可能。
    1.不能成功插入线性基。
    2.可以成功插入线性基。

    接下来分两类讨论性质1的证明

    不能成功插入线性基

    如果是不能成功插入的话,那么一定是与性质2相悖,所以就可以显然得出这个式子: x^d[a]^d[b]^...=0
    则可以推出 d[a]^d[b]^...=x
    所以如果x不能插入线性基,一定是前面以及有一些数的异或和为x。满足性质1。

    能够成功插入线性基

    假设这个数插入到了第i个位置,则可以得出 x^d[a]^d[b]^...=d[i]
    能推出 d[i]^d[a]^d[b]^...=x
    所以这种情况也能满足性质1。


    性质1得证。

    性质2的证明

    证法显然,但是还是说一下吧。
    反证法:假设d[a]^d[b]^...d[i]=0(假设d[i]比d[a]等晚入线性基)
    则可以推出d[a]^d[b]^...=d[i],而这样根据入线性基方案,d[i]不可能入线性基,所以假设不成立。

    所以性质2得证。

    性质3的证明

    还是分类讨论

    如果序列中所有元素都被插入线性基中

    由于所有元素都要入线性基,故不管用什么顺序都是一样的,所以就能保证最优性,即满足性质3.

    如果有元素没有被插入线性基中

    设这个元素为x,则一定满足一个d[a]^d[b]^d[c]=x的式子,则我们改变一些顺序。(由于a,b,c都是对称的,则只用考虑c即可)
    我们可以得到这个式子d[a]^d[b]^x=d[c]
    所以d[c] 就无法插入线性基了,所以总数还是一样的,所以改变顺序并不会改变插入数量,所以数量是一定的。

    性质3得证。

    所以来看一道线性基的题目

    https://www.luogu.com.cn/problem/P4301

    这道题就是一道经典线性基的题目

    解析:这道题怎么做呢?这道题和Nim游戏很像,唯一的区别就是第一次和第二次可以随便取。所以我们只要解决这唯一的不同点也就解决这个问题了。
    这道题要让取完之后无论怎么拿都不会出现异或和为0的情况,所以我们正好可以用线性基来维护,但是我们的入线性基的顺序是什么呢?
    这里有个贪心,那就是说a^b^c...<=a+b+c+...,这个的证法显然我就不说了。
    所以我们根据这个贪心,可以确定是要从大到小的顺序依次考虑入线性基,所以我们一开始就要排个序。
    根据题意,如果入线性基成功,那就没有事了,但是如果失败就要统计上ans,所以单独写一个入线性基add的时候要定义的是bool类型的函数,以便判断入线性基是否成功。
    最后再考虑-1 的事情,可以构造出一种情况,无论如何先手也不会输,那就是取得只剩1堆,之后先手就直接去完这剩下的一堆即可,所以是不可能输出 -1 的。

    所以正解代码如下:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int NR=105;
    int a[NR];
    int d[NR];
    ll ans;
    bool add(ll x)
    {
        for(int i=30;i>=0;i--)
        {
            if((x>>i)&1)
            {
                if(d[i]) x^=d[i];
                else
                {
                    d[i]=x;
                    return 1;
                }
            }
        }
        return 0;
    }
    bool cmp(int x,int y)
    {
        return x>y;
    }
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
        while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
        return x*f;
    }
    int main()
    {
    //    freopen("1.in","r",stdin);
    //    freopen("1.out","w",stdout);
        int n=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        sort(a+1,a+n+1,cmp);
        for(int i=1;i<=n;i++)
        {
            bool flag=add(a[i]);
            if(!flag) ans+=a[i];
        }
        printf("%lld",ans);
        return 0;
    }

     

  • 相关阅读:
    使用js给数组去重的3种常用方法
    JS操作DOM元素属性和方法
    打开新窗口并运行代码
    html5 中doctype 格式
    html htm shtml 区别
    RML Utilities for SQL Server工具
    [转载]项目经理和产品经理的区别
    sublime text 3 快捷键大全以及配置编译环境
    CSS的四种引入方式
    软件系统需求分析策划方案
  • 原文地址:https://www.cnblogs.com/chen-1/p/12731395.html
Copyright © 2011-2022 走看看