HDU 3949 异或线性基

该博客介绍了如何利用异或线性基解决一道编程竞赛题目,即找出n个数在所有可能的异或组合中第k小的数。通过构建异或线性基,并结合高斯消元方法,分析了线性基组合的性质,提出将二进制分解来确定线性基的组合。最后提供了实现代码。
摘要由CSDN通过智能技术生成

题目链接


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


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

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

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

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

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

故对于 t o t tot tot个线性基,线性组合的情况一共有 2 t o t − 1 2^{tot} - 1 2tot1种。
但这样并没有包括 0 0 0的情况。

所以可以特判一下,若 n n n个向量全部都用来构成线性基,则不会存在异或后为 0 0 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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值