線性相關/線性基
線性基有啥用
我們有些時候會遇到類似這樣的問題:給定一組數字,求異或和最大值。
我們可以用線性基來解決這個問題。
怎麼構建線性基呢
那麼我們怎麼考慮這個問題呢?
我們可以類比於向量。
在向量中,我們可以用單位向量表示某一個方向上的單位量。並且我們能用n個單位向量匯出一個$n$維的空間。
同理對於異或空間也是可以線性基來匯出的。
我們可以把一個長整型看成$64$維的向量,每個方向為$0$或$1$。
我們考慮一個數能匯出怎樣的一個單位向量呢?
我們發現如果是單位向量,那麼這個向量除了當前這個方向上為$1$,其餘都是$0$。
但是在這裡我們可以放寬這個要求,我們要求高位上全為$0$,這一位為$1$。
如果前$k$維的單位向量都已經被成功匯出,那麼我們就可以完全地掌控前$k$維的空間了。
大致就是這個意思。
我們考慮插入一個數的操作。
我們從高位(維)向低位(維)遍歷,如果我們發現當前第$i$位上為$1$,也就說明了這一個數對這個$i$維空間有影響。
我們判斷一下,當前這個空間是否已經被成功匯出了,如果已經被匯出的話,我們就有辦法消除這個影響,也就是異或上當前這個空間的單位向量。
如果沒有被成功匯出,那麼我們最高就能影響到當前這個空間了,記錄這個向量為當前空間的單位向量,結束迴圈。
有沒有插入失敗的情況呢?
有,就是當前這個向量能被完全消除影響,也就是說能被之前的向量匯出的時候。
線性相關
給定一個線性空間,如果一個向量存在於這個空間中,就是說這個向量和這個空間線性相關。
這個線性空間是由一個向量集合匯出的,具體見上面線性基的介紹。
常用模板
下面給一些常用的模板
插入元素
1 void Insert(ll a){ 2 for(int i=63;i>=0;i--)if(a&(1LL<<i)){ 3 if(num[i])a^=num[i]; 4 else { 5 num[i]=a;View Code6 return; 7 } 8 } 9 }
查詢某元素能否被表出
1 bool Inside(ll x){ 2 if(!x)return 1; 3 for(int i=63;i>=0;i--)if(x&(1ll<<i)){ 4 if(num[i])x^=num[i]; 5 else return 0; 6 if(!x)return 1; 7 } 8 return 0; 9 }View Code
查詢能表出的最大值
1 ll query_maxn(){ 2 ll now=0; 3 for(int i=63;i>=0;i--) 4 if((now^num[i])>now) 5 now^=num[i]; 6 return now; 7 }View Code
查詢能表出的最小值
1 ll query_minn(){ 2 for(int i=0;i<=63;i++) 3 if(num[i])return num[i]; 4 return 0; 5 }View Code
查詢表出的第k小值
我們要查詢第$k$小值,首先必須得讓每一個單位向量能影響的範圍獨立,也就是說當前向量的選取對其他的向量沒有影響。
之前我們保證了高位獨立,這時候我們要重新建基,建一個新基,保證高位獨立並且低位也獨立。
並且按照獨立團的個數進行重編號,那麼第k大的數就相當於重編號後的$k$所對應的二進位制數的異或和
1 void rebuild(){ 2 for(int i=1;i<=63;i++) 3 for(int j=i-1;j>=0;j--) 4 if(num[i]&(1LL<<j)) 5 num[i]^=num[j]; 6 for(int i=0;i<=63;i++) 7 if(num[i]) 8 p[cnt++]=d[i]; 9 } 10 ll query_kth(ll k){ 11 ll now=0; 12 if(k>=(1LL<<cnt))return -1; 13 for(int i=63;i>=0;i--) 14 if(k&(1LL<<i)) 15 now^=p[i]; 16 return now; 17 }View Code