1. 程式人生 > >線性基處理異或運算——模板+例題

線性基處理異或運算——模板+例題

線性基:處理異或操作的強大工具,思想也是可以借鑑的。
作用:用於處理多個數中選取一些數的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; } };

小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;
}