bzoj 2844: albus就是要第一个出场 线性基

首先线性基是什么呢。我们考虑我们有n个数。子集数量为2^n个。我们将每个子集内的数全部异或起来。得到一个值。但是我们考虑这些值内会可能存在重复的,太多了。不便于运算。所以我们考虑,能不能除去重复的。

我们假定n个数都是<10^9。我们考虑使用一个30*30的矩阵。其中其中第一行,存一个最高位1位于数字第1位的数。第二行存一个最高位1位于数字2位的数。以此类推。这样子我们可以得到一个30*30的的矩阵。这个矩阵未必每一行都填满。我们可以考虑,加入一个元素,发现他对应的行已经被填满了。我们将这个数和填在这里的位置xor起来。然后继续尝试加入。直到其被xor为0时,停止尝试。

我们考虑一下这个矩阵有什么性质。首先这里的最多m个非零数组成2^m个子集。然后我们可以得出。这里的每个子集的内容异或起来,包含原2^n个子集所有可能结果。为什么呢。我来简单的说一下。现在我们已经对[1,n]的元素进行了处理。然后得到了[1,m]的矩阵。然后我们要处理第n+1个元素。我们先假设之前的操作符合给出的性质。然后我们来考虑第n+1个元素是否依旧符合。如果n+1个能成功加入矩阵。则显然没有问题。如果无法加入,证明n+1在矩阵中被异或成了0。一个数为什么会被异或成0。xor符合交换律。然后我们知道显然矩阵中的元素异或出了n+1这个数,然后与n+1异或,成了0。所以我们可以证明,这个性质是成立的。

对于这道题而言。这样子我们可以轻松的求出有多少是重复的。然后这道题还需要得出一个特殊的性质,才能做出来。即任何重复的元素的重复次数是相同的。为什么呢。我也来先简单严谨的证明一下。我们考虑一下我们有2^(n - m)个办法得出0。这非常显然,因为没成功加入的元素组成的任意一个子集异或得出的值都可以在矩阵中得出。然后再异或一下,显然值为0。这意味着下界为2^(n - m)我们再考虑任何一个数的所有表示方式,进行异或都可以得出另外一个数。如果有任意一个数的数量超过了2^(n - m)则就超过了总数。所以上界也是这个。所以每一个重复数量都是2^(n - m)。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cmath>
 4 #include <algorithm>
 5 using namespace std;
 6 const int mo = 10086;
 7 const int MAXN = 110000;
 8 int n,q,tx,ans,m;
 9 int mp[40],jz[40],jc[40],jl[MAXN];
10 int find(int x)
11 {
12     for (int i = 31;i >= 1;i--)
13         if (x & jz[i]) 
14             return i;
15 }
16 int ksm(int x,int k)
17 {
18     if (!k) return 1;
19     if (k == 1) return x;
20     int t = ksm(x,k >> 1);
21     if (k & 1) return t * t % mo * x % mo;
22     return t * t % mo;
23 }
24 void solve(int x)
25 {
26     for (int i = 31;i >= 1;i--)
27     {
28         if (x & jz[i])
29         if(mp[i]) x ^= mp[i];else
30         {
31             mp[i] = x;
32             m++;
33             return;
34         }
35     }
36 }
37 int main()
38 {
39     scanf("%d",&n);
40     jz[1] = 1;
41     for (int i = 2;i <= 31;i++)
42         jz[i] = jz[i - 1] << 1;
43     jc[0] = 1;
44     for (int i = 1;i <= 31;i++)
45         jc[i] = jc[i - 1] << 1;
46     for (int i = 1;i <= n;i++)
47     {
48         scanf("%d",&jl[i]);
49         solve(jl[i]);
50     }
51     scanf("%d",&q);
52     int c = m;
53     for (int i = 31;i >= 1;i--)
54     {
55         if (mp[i] == 0) continue;
56         c--;
57         if (q & jz[i]) ans += jc[c];
58         ans %= mo;
59     }
60     ans = ans * ksm(2,n - m) % mo + 1;
61     ans %= mo;
62     printf("%d\n",ans);
63     return 0;
64 }
65 
posted @ 2020-01-20 08:41  IAT14  阅读(144)  评论(0编辑  收藏  举报