【线性基】bzoj2844: albus就是要第一个出场

线性基求可重rank

题目描述

给定 n 个数 $\{ a_i \}$ ,以及数 $x$。

将 $\{ a_i \}$​ 的所有子集(包括空集)的异或值从小到大排序,得到 $\{ b_i \} $。

求 $x$ 在 $\{ b_i \}$ 中第一次出现的下标。保证 $x$ 在 $\{ b_i \}$ 中出现。

HINT

数据范围:

1 <= N <= 10,0000

其他所有输入均不超过10^9


题目分析

考虑线性基求rank的过程,是一个求第k大的逆过程。也就是首先对线性基消元,再把线性基的元素给排出来,继而考虑每一位被线性基内第几个元素控制。

而现在是一个可重集,于是{1,1}和{1,1,1,1,1}就成了截然不同的两种情况。记线性基内有$legal$个元素,那么剩下就是$n-legal$个可被线性基表示的数字(下面简称无关数字)。沿用线性基题的一类思维方式,考虑这类无关数字对于线性基的影响。我们会发现,不论选出哪一部分无关数字(共$2^{n-legal}$种情况),都有且仅有一种“在线性基内选数”的方式使得选出的无关数字被重新异或为0.也就是说,记原rank为$preRank$,可重集rank就是$preRank*2^{n-legal}+1$。

对了,求原rank时候记得要和第k大操作一样把线性基元素重新拎出来。

 1 #include<bits/stdc++.h>
 2 const int maxn = 100035;
 3 const int MO = 10086;
 4 
 5 int n,p[103],ans,legal;
 6 
 7 int read()
 8 {
 9     char ch = getchar();
10     int num = 0, fl = 1;
11     for (; !isdigit(ch); ch = getchar())
12         if (ch=='-') fl = -1;
13     for (; isdigit(ch); ch = getchar())
14         num = (num<<1)+(num<<3)+ch-48;
15     return num*fl;
16 }
17 void insert(int x)
18 {
19     for (int i=30, chk=0; i>=0&&!chk; i--)
20         if (x>>i){
21             if (p[i]) x ^= p[i];
22             else{
23                 p[i] = x, chk = 1, ++legal;
24                 for (int j=30; j>i; j--)
25                     if (p[j]>>i) p[j] ^= p[i];
26             } 
27         }
28 }
29 void query(int x)
30 {
31     int i,j;
32     for (i=0, j=0; i<=30; i++)
33         if (p[i]) p[j] = i, ++j;
34     for (i=j-1; i>=0; i--)
35         if ((1<<p[i])&x) ans += (1<<i);
36 }
37 int qmi(int a, int b)
38 {
39     int ret = 1;
40     for (a%=MO; b; b>>=1, a=a*a%MO)
41         if (b&1) ret = ret*a%MO;
42     return ret;
43 }
44 int main()
45 {
46     n = read();
47     for (int i=1; i<=n; i++) insert(read());
48     query(read());
49     ans = (1ll*ans*qmi(2, n-legal)%MO+1)%MO;
50     printf("%d\n",ans);
51     return 0;
52 } 

 

 

END

posted @ 2018-12-31 22:04  AntiQuality  阅读(127)  评论(0编辑  收藏  举报