hdu3949 XOR (线性基(高斯消元))

hdu3949 XOR

原题地址http://acm.hdu.edu.cn/showproblem.php?pid=3949

题意:
T组数据,
每组数据给n个数,m个询问,对于每个询问给出一个k,询问给的n个数,选取任意非空子集,能异或出的数中第k小的,重复的数不计算。
数据范围
T<=30,1<=N<=10000,1<=Q<=10000
each number is between 1 and 10^18

题解:
理解线性基性质的好题。这个大佬的博文讲的非常透彻

消法一般有两种,一种是直接消成上三角阵,一种是消成对角线阵
第一种跑得比较快,但第二种贪心起来比较价简单,也算各有利弊
这样是因为譬如1,3两个数,用第一种消法得到1,3两个线性基,然而因为3^1=2,所以贪心起来要多判断一些东西
第二种消法直接得到1,2,从大到小选线性基异或只会越来越大
同时对角阵有一个特性是有线性基的位上只有线性基的那一位为1,其余均为0
但是这里要注意没有线性基的位不一定为0,譬如只有一个数3
——引用自一个大佬的博客

这题能够二进制拆分逐位去选,也就因为线性基在消成对角阵后,每一位的基互不影响,
如果有n个基,每一个都有选与不选两种方案,
除去空集,能够组成的数的数量就有2^k-1,
正好与二进制数相契合。

但是由于线性基保证不能异或出0,
所以这题还要判断原数组能否异或出0。
就是如果高消后的基的数量s小于n,
就说明原数组中存在一个数,它不能为线性基带来任何贡献,
它没有加入线性基的原因,是它的每一位在加入之前就被异或成了0,
因此,如果高消后的基的数量s小于n,则可以异或出0,k- -。

于是把k二进制拆分,从高位到低位,如果第k大的位有1,就选上第k大的基。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
const int N=10005;
int T,n,m,size;
LL a[N];
int gauss()
{
    int cnt=0;LL top=1LL<<60;
    for(LL i=top;i;i>>=1)
    {
        int j=-1;
        for(int k=cnt+1;k<=n;k++)
        if(a[k]&i) {j=k; break;}
        if(j==-1) continue; else swap(a[cnt+1],a[j]);
        for(int j=1;j<=n;j++)
        {
            if(j==cnt+1||(a[j]&i)==0) continue;
            a[j]=a[j]^a[cnt+1];
        }
        cnt++; if(cnt==n) break;        
    }
    return cnt;
}
LL query(LL k)
{
    k-=(n>size);
    if(k==0) return 0;
    if(k>=(1LL<<size)) return -1;
    LL ans=0;
    for(int i=size;k;k>>=1,i--)
    if(k&1) ans=ans^a[i];
    return ans;
}
int main()
{
    scanf("%d",&T);
    for(int t=1;t<=T;t++)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        size=gauss();
        scanf("%d",&m);
        printf("Case #%d:\n",t);
        for(int i=1;i<=m;i++)
        {
            LL k; scanf("%lld",&k);
            printf("%lld\n",query(k));  
        }
    }   
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值