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

 

posted @ 2018-01-01 20:54  Troywar  阅读(193)  评论(0编辑  收藏  举报