线性基入门

线性基是向量空间的一组基,通常可以解决有关异或的一些题目。

异或的重要性质:

  • \(如果\ a\ xor \ b \ xor \ c = 0,那么a \ xor \ b = c;\)
  • \(如果\ a \ xor \ b = c,那么\ a \ xor \ c = b;\)

线性基的重要性质:

  • 原序列里面的任意一个数都可以由线性基里面的一些数异或得到
  • 线性基里面的任意一些数异或起来都不能得到 0
  • 线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的。注意:线性基不唯一,只是元素数量唯一而已
  • 线性基二进制最高位互不相同

构造线性基模板:

 1 void L_B(ll x){
 2     for(int i=62;i>=0;i--){
 3         if(!(x>>(ll)i)) continue;
 4         if( !p[i] ){
 5             p[i]=x;
 6             break;
 7         }
 8         x^=p[i];
 9     }
10 }

 判断能否异或出一个数\(x\):

1 bool check(int x){
2     for(int i=62;i>=0;i--){
3         if( !(x>>(ll)i) ) continue;
4         if( !p[i] ) return false;
5         x^=p[i];
6     }
7     if( x==0 ) return true;
8     return false;
9 }

求最大值:(在原序列中,取若干个数,使得它们的异或和最大)

1 ll t_max()
2 {
3     ll ans=0;
4     for(int i=62;i>=0;i--)    //记得从线性基的最高位开始
5     if((ans^d[i])>anss)ans^=d[i];
6     return ans;
7  }   

 

求最小值:(两种情况)

  1. 用线性基内的元素能异或出的最小值:就是线性基里最小的那个数了,因为最小的无论异或上谁都要变大
  2. 整个序列能异或出的最小值:看一看有没有元素不可以插入进线性基中,如果有,最小值就是0,否则就是线性基中最小的那个数

求第k小的值:(从原序列中取若干元素进行异或,求出能异或出的所有数字中第k小的那个)

根据性质4,我们将线性基改造成每一位相互独立。具体地,\(如果j< i,p_{i}的第j位是1,我们就将p_{i}异或上p_{j}\),经过一系列的操作,对于二进制的某一位\(i\),\(只有p_{i}的这一位是1,其他的都是0\)。查询的时候就将\(k\)二进制拆分,对于是1的位,就异或上对应的线性基。最终得出的答案就是第\(k\)小值。

例题:xor

AC_Code:\(求第k小的核心代码:rebuild+k_th\)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 typedef long long ll;
 6 
 7 ll d[65],p[65];
 8 int cnt;
 9 void init(){
10     memset(d,0,sizeof(d));
11     memset(p,0,sizeof(p));
12     cnt=0;
13 }
14 
15 void L_B(ll val){
16     for(int i=62;i>=0;i--){
17         if (!(val>>(ll)i) ) continue;
18         if( !d[i] ){
19             d[i]=val;
20             break;
21         }
22         val^=d[i];
23     }
24 }
25 
26 void rebuild(){
27     for (int i=62;i>=0;i--)//重构
28         for (int j=i-1;j>=0;j--)
29             if (d[i]&(1ll<<j))
30                 d[i]^=d[j];
31 
32         for(int i=0;i<=62;i++)//转存
33             if (d[i])
34                 p[cnt++]=d[i];
35 }
36 
37 ll k_th(ll k){
38     ll ret=0;
39     if (k>=(1ll<<cnt)) return -1;
40     for (int i=62;i>=0;i--)
41         if (k&(1ll<<i))
42             ret^=p[i];
43     return ret;
44 }
45 
46 int main()
47 {
48     int T,n,q;
49     scanf("%d",&T);
50     for(int cas=1;cas<=T;cas++){
51         init();
52         scanf("%d",&n);
53         for(int i=0;i<n;i++){
54             ll k;
55             scanf("%lld",&k);
56             L_B(k);
57         }
58         scanf("%d",&q);
59         printf("Case #%d:\n",cas);
60         rebuild();
61         while(q--){
62             ll k;
63             scanf("%lld",&k);
64             if(n!=cnt) k--;
65             ll ans=k_th(k);
66             printf("%lld\n",ans);
67         }
68     }
69     return 0;
70 }

 

posted @ 2020-07-21 18:07  swsyya  阅读(193)  评论(0编辑  收藏  举报

回到顶部