1. 程式人生 > >hdu3949 XOR (線性基(高斯消元))

hdu3949 XOR (線性基(高斯消元))

hdu3949 XOR

題意:
T組資料,
每組資料給n個數,m個詢問,對於每個詢問給出一個k,詢問給的n個數,選取任意非空子集,能異或出的數中第k小的,重複的數不計算。
資料範圍
T<=30,1<=N<=10000,1<=Q<=10000
each number is between 1 and 10^18

消法一般有兩種,一種是直接消成上三角陣,一種是消成對角線陣
第一種跑得比較快,但第二種貪心起來比較價簡單,也算各有利弊
這樣是因為譬如1,3兩個數,用第一種消法得到1,3兩個線性基,然而因為3^1=2,所以貪心起來要多判斷一些東西
第二種消法直接得到1,2,從大到小選線性基異或只會越來越大
同時對角陣有一個特性是有線性基的位上只有線性基的那一位為1,其餘均為0
但是這裡要注意沒有線性基的位不一定為0,譬如只有一個數3
——引用自

一個大佬的部落格

這題能夠二進位制拆分逐位去選,也就因為線性基在消成對角陣後,每一位的基互不影響,
如果有n個基,每一個都有選與不選兩種方案,
除去空集,能夠組成的數的數量就有2^k-1,
正好與二進位制數相契合。

但是由於線性基保證不能異或出0,
所以這題還要判斷原陣列能否異或出0。
就是如果高消後的基的數量s小於n,
就說明原陣列中存在一個數,它不能為線性基帶來任何貢獻,
它沒有加入線性基的原因,是它的每一位在加入之前就被異或成了0,
因此,如果高消後的基的數量s小於n,則可以異或出0,k- -。

於是把k二進位制拆分,從高位到低位,如果第k大的位有1,就選上第k大的基。

程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
const int N=10005;
int T,n,m,size;
LL a[N];
int gauss()
{
    int cnt=0;LL top=1LL<<60;
    for(LL i=top;i;i>>=1)
    {
        int j=-1;
        for(int
k=cnt+1;k<=n;k++) if(a[k]&i) {j=k; break;} if(j==-1) continue; else swap(a[cnt+1],a[j]); for(int j=1;j<=n;j++) { if(j==cnt+1||(a[j]&i)==0) continue; a[j]=a[j]^a[cnt+1]; } cnt++; if(cnt==n) break; } return cnt; } LL query(LL k) { k-=(n>size); if(k==0) return 0; if(k>=(1LL<<size)) return -1; LL ans=0; for(int i=size;k;k>>=1,i--) if(k&1) ans=ans^a[i]; return ans; } int main() { scanf("%d",&T); for(int t=1;t<=T;t++) { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); size=gauss(); scanf("%d",&m); printf("Case #%d:\n",t); for(int i=1;i<=m;i++) { LL k; scanf("%lld",&k); printf("%lld\n",query(k)); } } return 0; }