线性基(求抑或最值)

  • Post author:
  • Post category:其他



线性基:

对一组数建立线性基得到:一组数a1,a2、、、an,其中ax:存最高位的1在第x位的元素值。


线性基作用

:线性基的子集的抑或和的 值域与原数抑或和的值域相同。


性质1:

线性基的任意子集的抑或和都不为0

构造线性基:

对原数组的每一个数p,从高位到低位扫描,找到第一位为1的,若该位上的线性基ai不存在,则ai=p,否则p=p^ai,继续扫描下一位。

像如果原数是2,3,则插入线性基中的是1,2

ll b[63], nb[63], tot=0,flag=false; //b为线性基  nb用来求第K小异或值,基线性基中数 tot为nb元素个数,flag为true表示线性基外有数

void insert(ll  x)
 { //插入
    for(int i = 62; i >= 0; i--) 
    {
        if(x & (1ll << i)) 
        {
            if(!b[i])
            {
                b[i] = x;
                return;
            }
            x ^= b[i];
        }
    }
    flag = true;
}

查询xor最大、小值:

ll Max(ll x) 
{  //求最大值
    ll res = x;
    for(int i = 62; i >= 0; i--) 
         res = max(res, res ^ b[i]);
    return res;
}

ll Min(ll x) 
{  //求最小值
    ll res = x;
    for(int i = 0; i <= 62; i++) 
        if(b[i])
            res ^= b[i];
    return res;
}

验证一个数x能否被xor出

bool fin(ll x) 
{ //验证存在性
    if(x == 0 && b[0])
        return 1;
    for(int i = 62; i >= 1; i--) 
    {
        int j = i - 1;
        if(x & (1 << j)) 
        {
            x ^= b[i];
            if(!x)
                return 1;
        }
    }
    return 0;
}

求抑或第k大

把k二进制拆分,如果k的第i位上是1,ans^=b[i]

ll b[63], nb[63], tot=0,flag=false; //b为线性基  nb用来求第K小异或值,基线性基中数 tot为nb元素个数,flag为true表示线性基外有数

void insert(ll  x)
 { //插入
    for(int i = 62; i >= 0; i--) 
    {
        if(x & (1ll << i)) 
        {
            if(!b[i])
            {
                b[i] = x;
                return;
            }
            x ^= b[i];
        }
    }
    flag = true;
}


ll Rebuild() {  //第K大
    for(int i = 62; i >= 0; i--) 
    {
         if(b[i] == 0)
             continue;
         for(int j = i - 1; j >= 0; j--) 
         {
             if(b[j] == 0)
                continue;
             if(b[i] & (1ll << j))
                b[i] ^= b[j];
         }
     }
     for(int i = 0; i <= 62; i++) 
     {
         if(b[i])
            nb[tot++] = b[i];
     }
}

ll Kth_Max(ll k) {
    if(flag)
         k--; 
    ll res = 0;
    if(k == 0)
        return 0;
    if(k >= (1ll << tot))
        return -1;
    for(int i = 62; i >= 0; i--) 
    {
        if(k & (1ll << i))
             res ^= nb[i];
    }
    return res;
}

实例


A – 元素

theme:给定n个魔石的编号和魔力值,如果一堆魔石中有子串的抑或和为0,则这堆魔石没有魔力。问最多能达到多少魔力值?

solution:按魔力值排序,依次插入线性基,如果该元素可以插入,说明该数某一为1的位有价值,则该魔石被选。

#include<bits/stdc++.h>
#define far(i,s,n) for(int i=s;i<n;++i)
typedef long long ll;
using namespace std;

ll b[63];
struct _a
{
    ll num;
    ll magic;
    bool operator<(_a b)const
    {
        return magic>b.magic;
    }
}a[2000];

void Insert(ll  x,int &flag)
 { //插入
    for(int i = 62; i >= 0; i--)
    {
        if(x & (1ll << i))
        {
            if(!b[i])
            {
                flag = 1;
                b[i] = x;
                return;
            }
            x ^= b[i];
        }
    }
}

int main()
{
    int n;
    cin>>n;
    far(i,0,n)
        scanf("%lld%lld",&a[i].num,&a[i].magic);
    sort(a,a+n);
    ll ans=0;
    far(i,0,n)
    {
        int flag=0;
        Insert(a[i].num,flag);
        if(flag)
            ans+=a[i].magic;
    }
    cout<<ans<<endl;
}

实例:求任选元素抑或和第k大


hdu3949:XOR

theme:给定n个数,q次询问,求任选元素抑或和第k大。

solution:套上面第k大模板即可,注意每组案例重置b、nb、flag、tot为0

#include<bits/stdc++.h>
#define far(i,s,n) for(int i=s;i<n;++i)
typedef long long ll;
using namespace std;

ll b[63], nb[63], tot=0,flag=false; //b为线性基  nb用来求第K小异或值,基线性基中数 tot为nb元素个数,flag为true表示线性基外有数

void Insert(ll  x)
 { //插入
    for(int i = 62; i >= 0; i--)
    {
        if(x & (1ll << i))
        {
            if(!b[i])
            {
                b[i] = x;
                return;
            }
            x ^= b[i];
        }
    }
    flag = true;
}


ll Rebuild() {  //第K大
    for(int i = 62; i >= 0; i--)
    {
         if(b[i] == 0)
             continue;
         for(int j = i - 1; j >= 0; j--)
         {
             if(b[j] == 0)
                continue;
             if(b[i] & (1ll << j))
                b[i] ^= b[j];
         }
     }
     for(int i = 0; i <= 62; i++)
     {
         if(b[i])
            nb[tot++] = b[i];
     }
}

ll Kth_Max(ll k) {
    if(flag)
         k--;
    ll res = 0;
    if(k == 0)
        return 0;
    if(k >= (1ll << tot))
        return -1;
    for(int i = 62; i >= 0; i--)
    {
        if(k & (1ll << i))
             res ^= nb[i];
    }
    return res;
}

int main()
{
    int t;
    cin>>t;
    for(int Case=1;Case<=t;++Case)
    {
        printf("Case #%d:\n",Case);
        memset(b,0,sizeof(b));
        memset(nb,0,sizeof(nb));
        tot=0,flag=false;
        int n;
        scanf("%d",&n);
        far(i,0,n)
        {
            ll x;
            scanf("%lld",&x);
            Insert(x);
        }
        Rebuild();
        int q;
        scanf("%d",&q);
        far(i,0,q)
        {
            ll k;
            scanf("%lld",&k);
            ll ans=Kth_Max(k);
            printf("%lld\n",ans);
        }
    }
}



版权声明:本文为wangqianqianya原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。