线性基 — 化繁为简

一. 异或的性质

1. A  xor A = 0

2. A xor 0 = A

3. 若 A xor B = C,则 A xor C = B,B xor C = A

二. 应用

1. 根据性质1,2,可以实现a,b交换

void swap()
{
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
}

2. 给定2n+1 个数,只有一个数只出现一次,将所有数异或,结果便是落单的数。

三. 线性基

1)什么是线性基?

基就是基底的意思,通过线性基中元素 xor 出的数的值域与原来的数组 xor 出数的值域相同,类似于空间 x轴、y轴、z轴的单位向量\overrightarrow{i} \overrightarrow{j} \overrightarrow{k},对单位向量乘上不同的系数,可以得到空间的所有向量,原数组相当于空间所有的向量,单位向量相当于线性基。

2)构造线性基:

  对每一个数 x 从高位到低位扫,扫到第 i 位为 1 时,若线性基中第 i 位不存在,则 a[i] = x 并结束此数的扫描,否则令x = x ^ a[i],继续往下扫。所以一个数要么加入线性基,要么变为0。

举个例子:先加入(1011)_2,最高位为1是第4位,则a[4] = (1011)_2,再加入(1001)_2,这个数也是最高位为1也是第4位,而 a[4] 已经存在,所以(1011)_2~~xor~~(1001)=(10)_2,同理会有a[2] = (10)_2,根据性质1,a[2] xor a[4] 就可以得到消失的(1001)_2

void Create_linear_basis(ll x)
{
    for(int i=62;i>=0;i--)
    {
        if(x&(1ll<<i))
        {
            if(!ans[i])
            {
                ans[i] = x;
                return ;
            }
            x = x ^ ans[i];
        }
    }
}

 

3)求原数组中异或的最大值(等价于求线性基中的异或最大值)

从高往低扫 a[x],若异或上 a[x] 使答案变大,则异或

题目:https://www.luogu.org/problemnew/show/P3812#sub

ll MAX_xor()
{
    ll ans = 0;
    for(int i=62;i>=0;i--)
    {
        if(ans^a[i] > ans)
            ans = ans^a[i];
    }
    return ans;
}

4)求原数组异或的最小值(相当于求线性基的最小值)

根据线性基的构造法则,线性基中存在值的最低位的值就是最小值

ll MIN_xor()
{
    for(int i=0;i<=62;i++)
    {
        if(ans[i])
            return ans[i];
    }
}

5)原数组通过任意次异或,能否得到x?

和构造线性基一样,如果最后 x 变为0,那就可以,当然需要特判一下x = 0,如果构造时有数最后为0,也就是 n!=m ,则就行

题目:https://www.nowcoder.com/acm/contest/180/D

bool Whether_xor(ll x)
{
    if(x==0) return n==m? false:true;//m为加入线性基元素的个数,n为原数组总的个数
    for(int i=62;i>=0;i--)
    {
        if(x&(1ll<<i))
        {
            if(!a[i])
              return false;
            else
              x = x ^ ans[i];
        }
    }
    return x==0? true:false;
}

6)求原数组第 k 小的异或值 

需要改造一下线性基,目的是让第x位为1的元素 (即a[x]) 在线性基中只有他的第x位为1(唯一性),比如 a[4] = (1011)_2,那就让其他元素的第四位都为0,处理完后再把a数组的元素从小到大依次放进b数组,求第k小时,只要把k二进制拆分,第j位是1就异或 b[j-1],比如求第(11)_2小,答案就是 b[0] ^ b[1],b数组相当于二进制位,(11)_2=2^0+2^1,只不过这里是b[0] ^ b[1],再举个例子:b[0] = (10)_2,b[1] = (100)_2,b[2] = (1000)_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的关系)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3949

#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll a[64];
ll b[64];
void linear_basis_a(ll x)    //构造线性基
{
    for(int i=62;i>=0;i--)
    {
        if(x&(1ll<<i))
        {
            if(!a[i])
            {
                a[i] = x;
                return ;
            }
            x = x ^ a[i];
        }
    }
}
int linear_basis_b()    //改造线性基
{
    int m = 0;
    for(int i=0;i<=62;i++)
    {
        for(int j=i-1;j>=0;j--)
        {
            if(a[i]&(1ll<<j))
                a[i] = a[i] ^ a[j];
        }
        if(a[i])
            b[m++] = a[i];
    }
    return m;               //上文中m
}
ll kth_xor(ll k,int m)      //求第k小
{
    ll ans = 0;
    for(int i=0;i<m;i++)
    {
        if(k&(1ll<<i))
            ans = ans ^ b[i];
    }
    return ans;
}
int main()
{
    int t,n,q,p;
    ll x,k;
    cin>>t;
    for(int i=1;i<=t;i++)
    {
        memset(a,0,sizeof(a));
        cin>>n;
        p = n;
        while(p--)
        {
            cin>>x;
            linear_basis_a(x);
        }
        printf("Case #%d:\n",i);
        int m = linear_basis_b();
        ull max_num = ((ull)1)<<m; //线性基性质(1)
        cin>>q;
        while(q--)
        {
            cin>>k;
            if(n!=m) k--;      //如果n!=m,则有一个最小值0
            if((ull)k>=max_num) cout<<-1<<endl;
            else cout<<kth_xor(k,m)<<endl;
        }
    }
    return 0;

7)线性基性质

 (1)线如果线性基有m个数,则最多可异或出 2^m-1个不同值

(2)线性基中元素任意异或,都不会等于0

(3)要注意n和m的关系

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值