HDU - 3949 XOR 线性基

这里讲解一下线性基是如何求取第 $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\n",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:\n",cas); 
        solve(); 
    }
    return 0; 
}

  

posted @ 2019-09-17 10:09  EM-LGH  阅读(114)  评论(0编辑  收藏  举报