这里讲解一下线性基是如何求取第 $k$ 小的:
首先,我们构建出线性基,然后从高位枚举 $d[i]$ 的每一位,发现如果有 $j<i$ 且 $d[i]$ 在二进制中的 $j$ 处为 $1,$ 则异或掉 $d[j].$
这么做会得到一个新的线性基,根据定理,线性基中元素互相异或,异或集合不变,所以是正确的.
然后,你会得到一个序列,将序列中每一个元素都整合到一起,开始查询.
你会发现新的序列中第 $i$ 个元素代表的恰好是第 $2^{i-1}$ 小的异或值.
而由于我们已经将高位含有低位的部分全部给异或没了,所以说每一次在新的线性基中异或低位只会使值更大,所以我们将 $k$ 二进制拆分,依次异或掉就是正确的了.
#include <cstdio> #include <cstring> #include <algorithm> #define ll long long #define M 62 #define N 100 #define setIO(s) freopen(s".in","r",stdin) using namespace std; int n,m; ll d[N]; void insert(ll x) { int i,j; for(i=M;i>=0;--i) { if(x&((ll)1<<i)) { if(!d[i]) { d[i]=x; break; } else x^=d[i]; } } } void init() { int i,j; for(i=M;i>=0;--i) { for(j=i-1;j>=0;--j) if(d[i]&(1ll<<j)) d[i]^=d[j]; } m=0; for(i=0;i<=M;++i) if(d[i]) d[m++]=d[i]; } ll query(ll k) { if(k==1&&m<n) return 0; if(m<n) --k; if(k>=(1ll<<m)) return -1; ll re=0; for(int i=M;i>=0;--i) if((k&(1ll<<i)) && d[i]) re^=d[i]; return re; } void solve() { ll a; int i,j,q; scanf("%d",&n); for(i=1;i<=n;++i) scanf("%lld",&a),insert(a); init(); scanf("%d",&q); for(i=1;i<=q;++i) scanf("%lld",&a),printf("%lld ",query(a)); memset(d,0,sizeof(d)); } int main() { int i,j,cas,T; // setIO("input"); scanf("%d",&T); for(cas=1;cas<=T;++cas) { printf("Case #%d: ",cas); solve(); } return 0; }