线性基-求异或最大,最小,第K小

定义:

基:在线性代数中,基(也称为基底)是描述、刻画向量空间的基本工具。向量空间的基是它的一个特殊的子集,基的元素称为基向量。向量空间中任意一个元素,都可以唯一地表示成基向量的线性组合。如果基中元素个数有限,就称向量空间为有限维向量空间,将元素的个数称作向量空间的维数。

同样的,线性基是一种特殊的基,它通常会在异或运算中出现,它的意义是:通过原集合S的某一个最小子集S1使得S1内元素相互异或得到的值域与原集合S相互异或得到的值域相同。

线性基的性质:

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

具体的构成可以去查看向量组的相关性!!!!!

构造线性基:保证线性基的元素满足以上的性质

void insert_num(int x)///初始线性基
{
    for(int i=61; i>=0; i--)
    {
        if(x&1ll<<i)
        {
            if(dp[i])
                x^=dp[i];///保证每个位置元素的唯一性
            else
            {
                dp[i]=x;
                break;
            }
        }
    }
}

以上的X就是我向线性基中插入的元素,DP代表我的线性基集合,例如原序列 1 2 3  线性基插入的就是 1  2  ,

1的二进制位01     2的二进制10       两个数异或 可以得到 3   11

求异或最大值

int  max_xor()
{
    int ans=0;
    for(int i=61;i>=0;i--)
        if(ans^dp[i]>ans)
         ans=ans^dp[i];
     return ans;
}

因为我们求得最大值,所以,我们从二进制高位开始枚举,因为异或后后面得低位会变,但是造成的影响小于最高位。

求异或最小值

int min_xor()
{
    int cnt=0;
    for(int i=0; i<=61; i++)
        if(dp[i])
            cnt++;
    if(cnt<n)///cnt代表线性基的个数,n代表原序列个数
        return 0;
    else
    {
        for(int i=0; i<=61; i++)
            if(dp[i])
                return dp[i];
    }

}

因为异或其他都会使结果变大,所以我们直接去线性基的最低位即可

求第K小

首先,如果我们要求第三小的异或值;现在线性基里第一小的01,第二小的10,第三小的就是11,是不是就是第一小与第二小的异或结果就可以表示第三异或小。

需要改造一下线性基,目的是让第x位为1的元素 (即a[x]) 在线性基中只有他的第x位为1(唯一性),比如 a[4] = ,那就让其他元素的第四位都为0,处理完后再把a数组的元素从小到大依次放进b数组,求第k小时,只要把k二进制拆分,第j位是1就异或 b[j-1],比如求第小,答案就是 b[0] ^ b[1],b数组相当于二进制位,,只不过这里是b[0] ^ b[1],再举个例子:b[0] = ,b[1] = ,b[2] = ,那么最小是b[0],其次是b[1],再其次是b[0]^b[1],接下来是b[2],b[2]^b[0],b[2]^b[1],最后是b[0]^b[1]^b[2],原理就和改造线性基有关,读者可以根据上文提到的唯一性结合二进制意会便可明白。(ps:同样要考虑n和m的关系)
 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=200;
int a[maxn];
int dp[maxn];///线性基
int dp1[maxn];///改造线性基
int cnt=0,n;///cnt代表线性基的元素个数,
void insert_num(int x)///初始线性基
{
    for(int i=61; i>=0; i--)
    {
        if(x&1ll<<i)
        {
            if(dp[i])
                x^=dp[i];///保证每个位置元素的唯一性
            else
            {
                dp[i]=x;
                break;
            }
        }
    }
}
void exchange()///改造线性基
{
    for(int i=0; i<=61; i++)
    {
        for(int j=i-1; j>=0; j--)
            if(dp[i]&(1ll<<j))///其他位变成0,保证唯一性
                dp[i]^=dp[j];
        if(dp[i])
            dp1[cnt++]=dp[i];
    }
}
int min_k_num(int k)///求第K小
{
    int ans=0;
    for(int i=0; i<cnt; i++)
    {
        if((k>>i)&1)
            ans^=dp1[i];
    }
    return ans;
}
int main()
{
    int k;
    scanf("%d %d",&n,&k);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
        insert_num(a[i]);
    }
    exchange();
    ull max_knum=1<<cnt;
    if(cnt<n&&k==1)
        cout<<"0"<<endl;
    else
    {
        if(cnt<n)
            k--;
        if(max_knum<k)///线性基能产生的最大元素个数小于求的K小
            cout<<"-1"<<endl;
        else
            cout<<min_k_num(k)<<endl;
    }
    return 0;
}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值