1. 程式人生 > >hdu3949 XOR(線性基)

hdu3949 XOR(線性基)

HDU3949 XOR(線性基)

題目:

給你\(n\)個數,從其中隨便取任意數問你第\(k\)小的異或和是多少。

題解:

這題是線性基的應用之一。我們知道一個集合的線性基可以異或出這個集合的所有異或和,並且方法唯一。對於一個數x能否被異或出來,我們可以這樣做,假設x的最高位為r,那麼線上性基裡面找到最高為也為r的數,讓x異或r。然後不斷重複這個操作,如果最後x能為0那麼肯定是能的。然後我們現在想想怎麼通過線性基求第k小的異或和。
首先,線性基可以看成一個最大線性無關組,也就是說最高位以上都是0。現在假設最大線性基是這樣的:

00100010 - - - - - - 4
00011000 - - - - - - 3
00000110 - - - - - - 2
00000001 - - - - - - 1

那麼異或和排序顯然是(用編號表示):
\(1\Rightarrow2\Rightarrow(2\bigoplus1)\Rightarrow3\Rightarrow(3\bigoplus1)\Rightarrow(3\bigoplus1\bigoplus2)\Rightarrow......\Rightarrow(4\bigoplus1\bigoplus2\bigoplus3)\)
這個排序顯然是按照二進位制最高位排序加上去的。但是如果想按這樣的規律數顯然是不行的。不過,我們可以聯絡下上面說的一個數x能否組成的原理。如果我將線性基用離散化的思想處理,然後再按照上面的排序思想。就可以把題目轉化乘k能否由離散後的線性基組成。
舉個例子:我們假設要求第k小的數,先上面的線性基離散化就可以看成

1000
0100
0010
0001

然後,這個新的線性基可以組成\(2^5-1\)排名內的所有數。也就是原線性基所有可以組成的異或的數量。k一定在這裡面,否則就不存在。因此,我們只需要分解k的所有1就行了。

程式碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
long long a[maxn],p[maxn],tot;
void Guass(int n)
{
    memset(p,0,sizeof(p));
    for(int i=1;i<=n;i++)
    {
        for(int j=63;j>=0;j--)
        {
            if((a[i]>>j)&1)
            {
                if(p[j]) a[i]^=p[j];
                else
                {
                    p[j]=a[i];
                    break;
                }
            }
        }
    }
    for(int i=63;i>=0;i--)
    {
        if(!p[i])continue;
        for(int j=i+1;j<=62;j++)
        {
            if((p[j]>>i)&1) p[j]^=p[i];
        }
    }
    tot=0;
    for(int i=0;i<=63;i++) if(p[i]) p[tot++]=p[i];
}
int main()
{
    int T,i,j,n,Q;
    long long k;
    scanf("%d",&T);
    for(int s=1;s<=T;s++)
    {
        printf("Case #%d:\n",s);
        scanf("%d",&n);
        for(i=1;i<=n;i++) scanf("%I64d",&a[i]);
        Guass(n);
        scanf("%d",&Q);
        while(Q--)
        {
            scanf("%I64d",&k);
            if(n!=tot)k--;
            if(k>=(1ll<<tot))printf("-1\n");
            else
            {
                long long ans=0;
                for(i=0;i<=63;i++) if((k>>i)&1) ans^=p[i];
                printf("%I64d\n",ans);
             } 
        }
    }
}