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

2021-04-24 07:29

阅读:415

标签:href   https   blank   进制   ase   ace   else   分解   范围   

题目链接: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
#includeusing namespace std;
#define maxn 10010
unsigned long long a[maxn];
int main(){
    int T,n;
    cin>>T;
    int dim;
    for(int kase=1;kase){
        scanf("%d",&n);
        for(int i=1;i"%llu",&a[i]);
        bool zero=0;//检查若干个数的异或是否可能是0 
        dim = n;//异或空间的基的维数 
        for(int i=1;i//最多循环64次,基的维数便确定 
            for(int j=i+1;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){
                        if(i!=k && (a[k]>>j & 1))a[k]^=a[i];
                    }
                    break;
                }
            } 
        }
        
//         cout
        int m;
        cin>>m;
        printf("Case #%d:\n",kase); 
        while(m--){
            unsigned long long  k,ans=0;
            scanf("%llu",&k);   
            if(zero)k--;//从这些数里面可以拼出0 
            if(k >= ((unsigned long long )1"-1");
            else{
                for(int i=dim-1;i>=0;i--){
                    if(k>>i & 1)ans^=a[dim-i];
                }
                printf("%llu\n",ans);
            }
        } 
    } 
}

 

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

标签:href   https   blank   进制   ase   ace   else   分解   范围   

原文地址:https://www.cnblogs.com/randy-lo/p/13265979.html


评论


亲,登录后才可以留言!