《算法竞赛进阶指南》0x35高斯消元与线性空间 异或空间

题目链接:https://www.acwing.com/problem/content/212/

给定n个数,要求这些数能够异或的数中的第k小,通过求异或线性基就可以得到这些数可以异或得到的不相同的数的维数,通过这些线性基的异或便可以得到这些数的异或的所有可能的数,

异或基中从大到小的数的最高位为1,其他数的这一位一定不是一,所以假设异或基的维数是4,则从大到小的数分别是第1000、0100、0010、0001小的数,所以只要将询问按照二进制进行分解就可以

,如果k的第i位是1,则取基中第dim-i个数异或到结果中去。

注意到我们的异或基都是非零的,所以如果从给的数里面能够得到0的话,第k大就相当于基构成的数中的第k-1大,如果不能得到0的话,就从我们的基中取第k小的,就可以。

还有一个注意点就是数据的范围,需要用unsigned long long 来进行存储,在scanf中使用的是%llu.

#include<iostream>
#include<cstdio>
using namespace std;
#define maxn 10010
unsigned long long a[maxn];
int main(){
    int T,n;
    cin>>T;
    int dim;
    for(int kase=1;kase<=T;kase++){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%llu",&a[i]);
        bool zero=0;//检查若干个数的异或是否可能是0 
        dim = n;//异或空间的基的维数 
        for(int i=1;i<=n;i++){//最多循环64次,基的维数便确定 
            for(int j=i+1;j<=n;j++){
                if(a[j]>a[i])swap(a[i],a[j]);
            }
            if(a[i]==0){
                dim=i-1;
                zero=true;
                break;
            }
            for(int j=63;j>=0;j--){
                if(a[i]>>j & 1){ 
                    for(int k=1;k<=n;k++){
                        if(i!=k && (a[k]>>j & 1))a[k]^=a[i];
                    }
                    break;
                }
            } 
        }
        
//         cout<<dim<<endl;
        int m;
        cin>>m;
        printf("Case #%d:
",kase); 
        while(m--){
            unsigned long long  k,ans=0;
            scanf("%llu",&k);   
            if(zero)k--;//从这些数里面可以拼出0 
            if(k >= ((unsigned long long )1<<dim))puts("-1");
            else{
                for(int i=dim-1;i>=0;i--){
                    if(k>>i & 1)ans^=a[dim-i];
                }
                printf("%llu
",ans);
            }
        } 
    } 
}
原文地址:https://www.cnblogs.com/randy-lo/p/13265979.html