1. 程式人生 > >hdu3949 XOR(線性基【第k大)

hdu3949 XOR(線性基【第k大)

題目連結

分析:
求第k大:把k二進位制拆分,如果k的第i位上是1,ans^=b[i]

這是什麼道理呢?
異或消元最後得到的是一組基
給出n個數能夠異或出來的值,都是這些基線性組合形成的數

方便起見,我們把矩陣消成對角線型(比較好理解)
這樣1只會出現在對角線上
cnt:對角線上有多少個1
顯然我們能得到1<<cnt個不同的異或值(包括0)

然而存在一個問題:0是否真的可行
我們需要特殊處理一下0:
如果cnt=n,就說明每個數字都有會貢獻1個1,那麼就絕對不可能取到0

if (cnt==n) x++;     //0不可以達到 

在最後統計答案的時候,我們預設0可以達到
所以只有x>now的時候才統計
這樣最後的x應該是等於1,這個1就是給0留的位置

for (int i=63;i>=0;--i) {
   if(b[i]) {
       ll now=(1LL<<(tmp-1));
       if(x>now) x-=now,ans^=b[i];       
       tmp--;
    }
}
#include<bits/stdc++.h>
#define ll long long

using namespace std;

const int N=10005
; int n,m,K; ll a[N],b[100]; void cal() { for (int i=1;i<=n;i++) for (int j=63;j>=0;j--) if (a[i]>>j&1) { if (b[j]) a[i]^=b[j]; else { b[j]=a[i]; for (int k=j-1;k>=0;k--) if
(b[k]&&(b[j]>>k&1)) b[j]^=b[k]; for (int k=j+1;k<=63;k++) if (b[k]>>j&1) b[k]^=b[j]; break; } } } int main() { int T; scanf("%d",&T); for (int cas=1;cas<=T;cas++) { memset(b,0,sizeof(b)); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%lld",&a[i]); cal(); int cnt=0; for (int i=0;i<=63;i++) if (b[i]) cnt++; //一共能組成(1<<cnt)個不同的數 scanf("%d",&m); printf("Case #%d:\n",cas); for (int i=1;i<=m;i++) { ll x; scanf("%lld",&x); if (cnt==n) x++; //0不可以達到 if (x>(1LL<<cnt)) {printf("-1\n");continue;} ll ans=0; int tmp=cnt; for (int i=63;i>=0;--i) { if(b[i]) { ll now=(1LL<<(tmp-1)); if(x>now) x-=now,ans^=b[i]; //x>now (0也要算上) tmp--; } } printf("%lld\n",ans); } } return 0; }