线性基处理异或运算——模板+例题

线性基:处理异或操作的强大工具,思想也是可以借鉴的。
作用:用于处理多个数中选取一些数的XOR的最大值,最小值,第k大值,并可以查询能否通过集合中任意个数XOR得到,时间复杂度为O(n*logn)
具体模板如下,思路和代码均参考于大佬的博客,感谢解惑~
链接:传送门

模板:

struct Linear_Basis
{
    int num[max_log];//插入数组
    void init()
    {
        memset(num,0,sizeof(num));
    }
    //将u插入num数组中
    void insert(int u)
    {
       int i;
       for(i=max_log;i>=0;i--)
       {
           if(u&(1<<i))//如果u的这一位有值,则进行判断
           {
               if(!num[i])//如果这位为0
               {
                   num[i]=u;//u赋值给它
               }
               else
               {
                   u^=num[i];//不为0,则u异或这个数
               }
           }
       }
    }
    /*
         操作之后u只有两种结果:
         1.被记录到了数组中
         2.未被记录,则此时u必定为0,说明此时的线性基已经能通过Xor得到u.
         因此我们可以用这种方法来判断此时是否存在Xor值为u的子集.
    */
    //查询操作
    bool check(int u)
    {
        int i;
        for(i=max_log;i>=0;i--)
        {
            if(u&(1<<i))//如果u这一位有数
            {
                if(!num[i])//num[i]这一位为0
                {
                    return 0;
                }
                else
                    u^=num[i];//否则u取异或num[i]
            }
        }
        return 1;
    }
    //查询最大值
    int aks_max()
    {
        int res=0,i;
        for(i=max_log;i>=0;i--)
        {
            if((res^num[i])>res)res^=num[i];
        }
        return res;
    }
    //查询最小值
    int ask_min()
    {
        int i;
        for(i=0;i<=63;i++)
        {
            if(num[i]) return num[i];//最小的非0数就是最小值
        }
    }
    //查询第k小/大
    /*
        首先将数组中的所有数变成包含这个最高位且可以通过Xor得到的最小值.
        具体实现方法就是每一位向后扫,若Xor后变小,则Xor.
        之后就可以发现第k大/小只要将k改为二进制后,将二进制所对应的位置的数Xor起来即可.
    */
    int ask_kth(int k)
    {
        int i,j,tmp[63],tt=-1,res=0;
        for(i=0;i<=63;i++)
        {
            for(j=i-1;j>=0;j--)
            {
                if(num[i]&(1 << j)) num[i]^=num[j];
            }
            if(num[i]) tmp[++tt]=num[i];
        }
        for(i=0;i<=tt;i++)
        {
            if((1 << i)&k) res^=tmp[i];
        }
        return res;
    }
};

加个模板题,也是让我知道线性基存在的题:https://www.nowcoder.com/acm/contest/180/D
题目描述:

小a有n个数,他提出了一个很有意思的问题:他想知道对于任意的x, y,能否将x与这n个数中的任意多个数异或任意多次后变为y

思路:注意到若x^一堆东西=y,则必有x^y=一堆东西,就转化成查询集合中任意数异或可不可以达到x^y这个数,也就是线性基模板题了~

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

const int maxn=1e5+10;
const int max_log=100;

int a[maxn];

struct Linear_Basis
{
    int num[max_log];//插入数组
    void init()
    {
        memset(num,0,sizeof(num));
    }
    //将u插入num数组中
    void insert(int u)
    {
       int i;
       for(i=max_log;i>=0;i--)
       {
           if(u&(1<<i))//如果u的这一位有值,则进行判断
           {
               if(!num[i])//如果这位为0
               {
                   num[i]=u;//u赋值给它
                   break;
               }
               else
               {
                   u^=num[i];//不为0,则u异或这个数
               }
           }
       }
    }
    /*
         操作之后u只有两种结果:
         1.被记录到了数组中
         2.未被记录,则此时u必定为0,说明此时的线性基已经能通过Xor得到u.
         因此我们可以用这种方法来判断此时是否存在Xor值为u的子集.
    */
    //查询操作
    bool check(int u)
    {
        int i;
        for(i=max_log;i>=0;i--)
        {
            if(u&(1<<i))//如果u这一位有数
            {
                if(!num[i])//num[i]这一位为0
                {
                    return 0;
                }
                else
                    u^=num[i];//否则u取异或num[i]
            }
        }
        if(u==0)return 1;
        else return 0;
    }
};
Linear_Basis xxj;
int main()
{
    int n,m;
    scanf("%d",&n);
    xxj.init();
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
        xxj.insert(a[i]);
    }
    scanf("%d",&m);
    while(m--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        int temp=x^y;
        if(xxj.check(temp))
        {
            printf("YES\n");
        }
        else
        {
            printf("NO\n");
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Q755100802/article/details/82723115
今日推荐