洛谷 P4869 albus就是要第一个出场(线性基,异或值排名)
传送门
解题思路
首先背过线性基的一个性质吧(因为我不会证明):
线性基里的每一个异或值出现的次数相等,为 \(2^{n-k}\) 。
然后这题就变成统计某个数是第几大异或值了。
分类讨论在归纳一下:
假设询问的数字是q。当处理到第i位且q的这一位为1,且线性基这一位有数,则
- 当前面位异或起来得到的这一位已经是1,则可以通过让其他数异或上a[i]来减小其他数,使其小于q。
- 当前面位异或起来得到的这一位是0,则q必须要异或上a[i],但是别的数可以通过不异或a[i]使其小于q。
综上,第i位对答案是否产生贡献只与q的第i位原来是否为1有关,与现在的状态无关。
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<vector>
using namespace std;
const int mod=10086;
int n,x,a[35],cnt,q,ans[35];
int ksm(int a,int b){
if(b==0) return 1;
if(b==1) return a;
int res=ksm(a,b/2);
if(b&1) return res*res%mod*a%mod;
return res*res%mod;
}
void insert(int x){
for(int i=30;i>=0;i--){
if(x&(1<<i)){
if(a[i]) x^=a[i];
else{
a[i]=x;
return;
}
}
}
}
int query(int x){
int res=0;
for(int i=30;i>=0;i--){
if((x&(1<<i))&&(a[i])){
res=(res+ksm(2,ans[i]))%mod;
}
}
return res;
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++) cin>>x,insert(x);
for(int i=0;i<=30;i++) if(a[i]) ans[i]=cnt++;
cin>>q;
cout<<(ksm(2,n-cnt)*query(q)+1)%mod;
return 0;
}