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;
}