HDU 3949 异或线性基

来源:互联网 发布:刻录软件nero免费下载 编辑:程序博客网 时间:2024/06/02 01:28

题目链接


题意:
n个数和q个询问,每个询问给出一个正整数k,问这n个数异或组合出的所有数中第k小的是什么数,若不存在则输出1


思路:
首先构建出这n个数的异或线性基。

假设非零线性基共有tot个,从小到大排列后,对于第x个线性基,前1(x1)个线性基无论怎样线性组合,其结果一定小于第x个线性基。

因为第x个线性基假设最高位的1在第k位,因为异或不存在进位,故比它小的基无论怎么组合都组合不出二进制位大于等于k的一个1

这样的话,线性基的线性组合大小排序很类似于二进制的大小排序,比如:
10000 一定比 01111,01101,01110等等形如0xxxx的二进制数大。

故可考虑对k二进制分解,若其第i位为1,则加上从小到大排序后第i小的线性基的贡献。

故对于tot个线性基,线性组合的情况一共有2tot1种。
但这样并没有包括0的情况。

所以可以特判一下,若n个向量全部都用来构成线性基,则不会存在异或后为0的情况。

故此题得解。

代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;typedef long long ll;const int A = 1e4 + 10;ll a[A],b[110],c[A];int n,zero,tot;void init(){    zero = tot = 0;    memset(b,0,sizeof(b));    for(int i=1 ;i<=n ;i++) for(int j=62 ;j>=0 ;j--){        if((a[i]>>j)&1){            if(b[j]) a[i] ^= b[j];            else{                b[j] = a[i];tot++;                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<=62;k++) if(((b[k]>>j)&1))         b[k] ^= b[j];                break;            }        }    }    zero = (tot<n);tot = 0;    for(int i=0 ;i<=62 ;i++) if(b[i]) c[tot++] = b[i];}ll solve(ll k){    if(zero) k--;    if(k >= (1LL<<tot)) return -1;    ll ans = 0;    for(int i=0 ;i<=62 ;i++) if((k>>i)&1) ans ^= c[i];    return ans;}int main(){    int T,_=1;scanf("%d",&T);    while(T--){        scanf("%d",&n);        for(int i=1 ;i<=n ;i++) scanf("%I64d",&a[i]);        init();        int q;scanf("%d",&q);        printf("Case #%d:\n",_++);        while(q--){            ll k;scanf("%I64d",&k);            printf("%I64d\n",solve(k));        }    }    return 0;}