HDU3949 线性基模板

Ac链接


  • 给定n个数,求子集异或和的第k大。\(n\le10^5,a_i\le10^9\)
  • 第一步肯定是构造线性基。设线性基的基底数量为k,那么子集异或和本质不同的个数为\(2^k\)(如果有为0的情况)。其实求第k大很简单,你把k拆分成2进制,对应基底从左到右的每一位,如果为1就异或上去就行了。不过我们需要分为两种情况,一个是存在异或和为0的情况,一个是不存在的。如果不存在异或和为0,需要把k+1。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100;
ll t,n,m,q,cnt,k,ans,a[N];
void insert(ll v){
    for(int i=63;i>=0;--i) if((v>>i)&1){
        if(a[i]) v^=a[i];
        else{
            for(int j=i-1;j>=0;--j)
                if((v>>j)&1) v^=a[j];
            for(int j=i+1;j<=63;++j)
                if((a[j]>>i)&1) a[j]^=v;
            a[i]=v;
            break;
        }
    }
}
int main(){
    scanf("%lld",&t);
    for(int cas=1;cas<=t;++cas){
        memset(a,0,sizeof(a));
        scanf("%lld",&n);ll x;
        for(int i=1;i<=n;++i){
            scanf("%lld",&x);
            insert(x);
        }
        cnt=0;
        for(int i=0;i<=63;++i) if(a[i]) cnt++;
        scanf("%lld",&m);
        printf("Case #%d:\n",cas);
        for(int i=1;i<=m;++i){
            scanf("%lld",&k);
            if(cnt==n) k++;
            ll ans=0;ll tmp=cnt;
            if(k>(1LL<<cnt)){printf("-1\n");continue;}
            for(int i=63;i>=0;--i){
                if(a[i]){
                    ll now=1LL<<(tmp-1);
                    if(k>now) k-=now,ans^=a[i];
                    tmp--;
                }
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/kgxw0430/p/10425276.html