HDU3949:XOR(高斯消元)(线性基)

传送门

题意

给出n个数,任意个数任意数异或构成一个集合,询问第k大个数

分析

这题需要用到线性基,下面是一些资料
1.高斯消元&线性基&Matirx_Tree定理 笔记
2.关于线性基的一些理解
3.线性基
这题操作步骤如下:
1.高斯消元求n个数的线性基
2.对于每个询问,遍历a[],如果(1<<p)&k==1,那么ans^=a[cnt-p],注意这里a[]大的标号小

trick

1.如果cnt!=n,那么线性基中存在某些数异或和为0,导致k--,解释如下

原数集能否异或出0呢?我们无法通过线性基中的数判断,但可以根据线性基的大小判断。如果线性基的大小与原数集大小相同,那么无法异或出0,否则可以。这个可以通过线性基的插入证明,即如果异或出0则不插入,导致线性基的大小小于原数集的大小。

代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
int t,n,q,cnt;
ll x,zero;
ll a[10010];

void gauss()
{
    cnt=zero=0;
    for(int p=62;p>=0;--p)
    {
        int j=cnt+1;
        while(j<=n&&(!(a[j]&(1LL<<p)))) j++;
        if(j==n+1) continue;
        cnt++;
        swap(a[cnt],a[j]);
        for(int i=1;i<=n;++i) if((i!=cnt)&&(a[i]&(1LL<<p))) a[i]^=a[cnt];
    }
    if(cnt!=n) zero=1;
}
ll query(ll x)
{
    x-=zero;
    ll ans=0;
    if(!x) return 0;
    if(x>=(1LL<<cnt)) return -1;
    for(int p=cnt;p>=0;--p)if(x&(1LL<<p)) ans^=a[cnt-p];
    return ans;
}

int main()
{
    scanf("%d",&t);
    for(int k=1;k<=t;++k)
    {
        printf("Case #%d:\n",k);
        scanf("%d",&n);
        for(int i=1;i<=n;++i) scanf("%lld",a+i);
        gauss();
        //for(int i=1;i<=cnt;++i) printf("%lld\n",a[i]);
        for(scanf("%d",&q);q--;)
        {
            scanf("%lld",&x);
            printf("%lld\n",query(x));
        }
    }
}
posted @ 2017-04-18 22:15  遗风忘语  阅读(152)  评论(0编辑  收藏  举报