【BZOJ 2844】: albus就是要第一个出场
题目大意:
给一个长度为n的序列,将其子集的异或值排序得到B数组,给定一个数字Q,保证Q在B中出现过,询问Q在B中第一次出现的下标。
题解:
感觉和hdu3949第K小异或值有一像,然而发现要求出现次数……emmmm
考虑线性基的性质,即在n个数字中求出其极大线性无关子集,设其长度为m,也就意味着有n-m个元素是可以用这m个元素表示的,考虑假设我们现在用这m个变量表示出了一个数字A,那么给A异或上0还是其本身,考虑剩下的n-m个元素可以凑多少个0,即二项式定理,所以可以知道,对于任意一个可以用线性基表示出来的数,其出现次数均为$2^{n-m}$。
代码:
1 #include "bits/stdc++.h" 2 3 using namespace std; 4 5 inline int read(){ 6 int s=0,k=1;char ch=getchar(); 7 while(ch<'0'|ch>'9') ch=='-'?k=-1:0,ch=getchar(); 8 while(ch>47&ch<='9') s=s*10+(ch^48),ch=getchar(); 9 return s*k; 10 } 11 12 const int N=1e5+5,mod=10086; 13 14 int a[N],bin[100],b[100],n,Q,m,cnt,ans; 15 16 inline int powmod(int a,int b){ 17 int ret=1; 18 while(b){ 19 if(b&1) ret=ret*a%mod; 20 b>>=1,a=a*a%mod; 21 }return ret; 22 } 23 24 int main(){ 25 n=read(); 26 register int i,j,k; 27 for(i=1;i<=n;++i) a[i]=read(); 28 for(i=0;i<=30;++i)bin[i]=1<<i; 29 for(i=1;i<=n;++i) 30 for(j=30;~j;--j) if(a[i]&bin[j]) 31 if(b[j]) a[i]^=b[j]; 32 else { 33 b[j]=a[i];++cnt; 34 for(k=j-1;~k;--k) if(b[k]&&(b[j]&bin[k])) b[j]^=b[k]; 35 for(k=j+1;k<=30;++k) if(b[k]&bin[j]) b[k]^=b[j]; 36 break; 37 } 38 for(i=0;i<=30;++i) if(b[i]) a[m++]=i; 39 Q=read(); 40 for(i=0;i^m;++i) if(Q&bin[a[i]]) 41 ans|=bin[i]; 42 ans%=mod; 43 printf("%d\n",(ans*powmod(2,n-cnt)%mod+1)%mod); 44 }
没有什么不可能。