P4869 albus就是要第一个出场

P4869 albus就是要第一个出场

1 题目描述

2 分析

  • 根据线性基的常识,我们知道可以把这n个数变成线性基,假设线性基里面的数的个数是k个。由于线性基产生的异或结果各不相同,所以我们知道这n个数一共有\(2^k\)种不同的结果。

  • 接下来我们要考虑这些不同的数重复的次数问题。每个结果重复了多少次呢?我们可以把问题等同于有一个n元一次方程组,如下所示:

    \[(a_1,a_2,...,a_n)\begin{pmatrix}x_1\\ x_2\\ ...\\ x_n\end{pmatrix}=b \]

    这里的\(a_i\)就是我们读入的第i个数,\(x_i\)就是方程组的解,在本题中就只有0和1两种可能性,b就是我们希望的异或结果。 由于我们知道a数组里面这n个列向量只有k个线性无关的,根据高斯消元,我们消元以后就只有k个主元,还有n-k个自由元,自由元每个元素都可以是0或者1,所以这个方程组就有\(2^{n-k}\)个解,所以每个b都在异或中出现了这么多次。

  • 于是这个题目我们只要计算出比B小的异或结果有几个,然后乘以\(2^{n-k}\),最后再加一,就是我们的答案了。 我们只要修改一下线性基,比如原来的线性基是\(1101_2, 101_2,10_2\),我们可以修改为\(1000_2,101_2,10_2\),这样的效果是等价的,但是方便我们计算第几大。如果我们要计算\(111_2\)是这里的第几大,我们可以先枚举d[3],但是\(111_2\)里面没有\(2^3\),所以我们不加d[3],然后我们枚举d[2],由于\(111_2\)里面有\(2^2\),所以我们可以\(101_2\)异或进去,这样比\(111_2\)小的就有了2个,然后我们看d[1],发现也必须要被异或进去,这样就变成了\(111_2\),又多了1个比他小的。所以比B小的数共计有3个,B是第4个数。

  • 时间复杂度:\(O(30\times n)\),本题主要的复杂度在于建立线性基。

3 代码

#include<bits/stdc++.h>
using namespace std; 
int const N=1e5+10;  
int a[N],n,b,d[40],num,t[40];  
int ins(int x){
    for(int i=30;i>=0;i--) 
        if(x&(1<<i)){
            if(d[i]) x^=d[i]; 
            else {
                d[i]=x; 
                return 1; 
            }
        }
    return 0; 
}
int main(){
    scanf("%d",&n); 
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]); 
        num+=ins(a[i]); 
    } 
    scanf("%d",&b);  
    for(int i=1;i<=30;i++)  
        for(int j=0;j<i;j++)  
            if(d[i]&(1<<j)) d[i]^=d[j];  
    int p=1;  
    for(int i=1;i<=n-num;i++)
        p=p*2%10086;  
    for(int i=1;i<=30;i++)  
        t[i]=t[i-1]+(d[i-1]>0); 
    int tmp=0,ans=0;  
    for(int i=30;i>=0;i--) {
        if(d[i]){
            if(b&(1<<i)) {
                tmp^=d[i];  
                ans+=(1<<t[i]); 
                ans%=10086;  
            }
        }
    }
    ans=ans*p%10086; 
    cout<<(ans+1)%10086<<endl;  
    return 0; 
}
posted @ 2020-06-12 14:50  zjxxcn  阅读(141)  评论(0编辑  收藏  举报