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

  • Post author:
  • Post category:其他


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;
}



版权声明:本文为Bfk_zr原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。