zoukankan      html  css  js  c++  java
  • 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:
    ",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
    ",a[i]);
            for(scanf("%d",&q);q--;)
            {
                scanf("%lld",&x);
                printf("%lld
    ",query(x));
            }
        }
    }
    
  • 相关阅读:
    mv命令(转)
    Linux获得命令帮助(学习笔记五)
    Shell解释器(学习笔记四)
    rmdir 命令(转)
    Java从零开始学十八(抽象类和接口)
    rm 命令(转)
    Centos6.6系统root用户密码恢复案例(转)
    Java从零开始学十七(简单工厂)
    Java从零开始学十六(多态)
    mkdir命令(转)
  • 原文地址:https://www.cnblogs.com/chendl111/p/6730853.html
Copyright © 2011-2022 走看看