hdu 3949 XOR

题目大意是给n个数,然后随便取几个数求xor和,求第k小的。(重复不计算)

首先想把所有xor的值都求出来,对于这个规模的n是不可行的。

然后之前有过类似的题,求最大的,有一种方法用到了线性基。

那么线性基能不能表示第k大的呢?

显然,因为线性基可以不重复的表示所有结果。它和原数组是等价的。

对于一个满秩矩阵

100000

010000

001000

000100

000010

000001

可以看出来最小的就是1,次小的是2,后面以此就是3,4,5,6....2^6-1.

可以看出来,每个向量基,都有取或者不取两种选择,而且把k二进制拆开来后,第i位就表示第i小的向量基取不取(1取,0不取)。

因为保证了第k大的基总大于比他小的基的线性组合。

此外,需要对非满秩的矩阵进行特判。因为其存在0的结果,如果要求最小,那么就是0。如果不是,那么就是求当前矩阵下的第(k-1)小。

然后接下来求的时候,需要对不存在的情况特判,因为每个数都有取或不取,即2^row-1种,除去全不取的情况。

/*
hdu 3949
题意:n个数异或成一个集合,求第k大 
*/

#include<bits/stdc++.h>
#define LL long long 
using namespace std;

struct Linear_Basis
{
    LL b[63],nb[63],tot;
    void init(){
        tot=0;
        memset(b,0,sizeof(b));
        memset(nb,0,sizeof(nb));
    }
    bool ins(LL x){
        for(int i=62;i>=0;i--)
            if (x&(1LL<<i))
            {
                if (!b[i]) {b[i]=x;break;}
                x^=b[i];
            }
        return x>0;
    }
    LL Max(LL x){
        LL res=x;
        for(int i=62;i>=0;i--)
            res=max(res,res^b[i]);
        return res;
    }
    LL Min(LL x){
        LL res=x;
        for(int i=0;i<=62;i++)
            if (b[i]) res^=b[i];
        return res;
    }
    void rebuild(){
        for(int i=62;i>=0;i--)
            for(int j=i-1;j>=0;j--)
                if (b[i]&(1LL<<j)) b[i]^=b[j];
        for(int i=0;i<=62;i++)
            if (b[i]) nb[tot++]=b[i];
    }
 
    LL Kth_Max(LL k,int n){
    	if(tot<n){
    		if(k==1) return 0;
    		else k--;
    	}
    	if(k>=(1LL<<tot)) return -1;
    	LL res=0;
        for(int i=62;i>=0;i--)
            if (k&(1LL<<i)) res^=nb[i];
        return res;
    }
 
} LB;
int main()
{
	int T;
	scanf("%d",&T);
	for(int t=1;t<=T;t++){
		printf("Case #%d:\n", t);
		LB.init();
		int n;
		scanf("%d",&n);
		for(int i=0;i<n;i++){
			LL x;
			scanf("%lld",&x);
			LB.ins(x); 
		}
		LB.rebuild();
		int q;
		scanf("%d",&q);
		while(q--){
			LL k;scanf("%lld",&k);
			printf("%lld\n",LB.Kth_Max(k,n));
		} 
	} 
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值