线性基入门
线性基是向量空间的一组基,通常可以解决有关异或的一些题目。
异或的重要性质:
- \(如果\ 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 }
求最小值:(两种情况)
- 用线性基内的元素能异或出的最小值:就是线性基里最小的那个数了,因为最小的无论异或上谁都要变大
- 整个序列能异或出的最小值:看一看有没有元素不可以插入进线性基中,如果有,最小值就是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 }