關於線性基的學習與理解 性質

學習來自:https://www.cnblogs.com/ljh2000-jump/p/5869991.html

和 https://blog.csdn.net/a_forever_dream/article/details/83654397

此處只拿出他們做題總結的部分,原理過程刪掉了

1、線性基:

  若干數的線性基是一組數a1,a2,...an,其中ax的最高位的1在第x位。

  通過線性基中元素xor出的數的值域與原來的數xor出數的值域相同。

2、線性基的構造法:

  對每一個數pp從高位到低位掃,掃到第x位爲1時,若ax不存在,則ax=pax=p並結束此數的掃描,否則令p=p   xor ax。

3、查詢:

  用線性基求這組數xor出的最大值:從高往低掃ax,若異或上ax使答案變大,則異或。

4、判斷:

  用線性基求一個數能否被xor出:從高到低,對該數每個是1的位置x,將這個數異或上axax(注意異或後這個數爲1的位置和原數就不一樣了),若最終變爲0,則可被異或出。當然需要特判00(在構造過程中看是否有p變爲0即可)。例子:(11111,10001)(11111,10001)的線性基是a5=11111,a4=01110,要判斷11111能否被xor出,11111 xora5=0,則這個數後來就沒有是1的位置了,最終得到結果爲00,說明11111能被xor出。

 

關於線性基的模板:

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll a[N],p[100];
int n;
int insert(ll x){
	
	for(int j=62;j>=0;j--) {
		if(!(x>>j)) continue; 
	 	if(!p[j]) { p[j]=x; return 1; }                 
			x^=p[j];
	}
	return 0;
}

一些性質:

你可以理解爲將一個序列處理完之後得到的產物,並且有如下性質(後面有證明):

1、原序列裏面的任意一個數都可以由線性基裏面的一些數異或得到。
2、線性基裏面的任意一些數異或起來都不能得到0 00
3、線性基裏面的數的個數唯一,並且在保持性質一的前提下,數的個數是最少的
 

證明性質1
       我們知道了線性基的構造方法後,其實就可以很容易想到如何證明性質1了,我們設原序列裏面有一個數x xx,我們嘗試用它來構造線性基,那麼會有兩種結果——1、不能成功插入線性基;2、成功插入線性基。

分類討論一下


1、不能成功插入線性基**


       什麼時候不能插入進去呢?
       顯然就是它在嘗試插入時異或若干個數之後變成了0 00。
       那麼就有如下式子:
       x  ^ d[a]^ d[b] ^ d[c] ^...=0 
       根據上面的那個小性質,則有:
       d[a] ^ d[b] ^ d[c] ^...=x
       所以,如果x不能成功插入線性基,一定是因爲當前線性基裏面的一些數異或起來可以等於x。

2、可以成功插入線性基


       我們假設x插入到了線性基的第i個位置,顯然,它在插入前可能異或若干個數,那麼就有:
       x ^ d[a] ^ d[b] ^ d[c]^ …=d[i]
       d[i]^ d[a]  ^ d[b] ^ d[c] ^ …=x
       所以顯然,x此時也可以由線性基裏面的若干個數異或得到。

綜上,性質1得證
再看性質2


       各位大佬肯定認爲這是一條顯然的性質啊,但爲了嚴謹一點,還是給出證明吧:
       我們使用反證法
       設d[a] ^ d[b]^ d[c]=0(其中d[c]比d[a]和d[b]要更晚被插入線性基)
       那麼有d[a] ^ d[b]=d[c] 
       ∵d[c] 可以由d[a]^ d[b]得到
       ∴d[c]不可能插入線性基

       故假設不成立,所以線性基中不存在有任何數異或起來可以得到0。

最後看性質3
       這個性質被BJWC拿來出過一道題,那題網上幾乎找不到證明(都是草草的給出做法然後貼代碼),然而如果你熟記這個性質3,那麼很快就能想明白那題。(這題在文章末尾會給出)
       那麼我來嘗試證明性質3(畢竟網上幾乎找不到證明作爲參考,這裏筆者我只能亂推了):

還是沒什麼卵用地分類討論一下


1、假如序列裏面的所有元素都可以插入到線性基裏面


       顯然如果是這種情況的話,不管是用什麼順序將序列裏的數插入到線性基裏,線性基中的元素一定與原序列元素數量相同。所以性質3成立。

2、假如序列裏面的一些元素不能插入到線性基裏面


       我們設x不能插入到線性基裏面,那麼一定滿足形如d[a] d[a]d[a] ^ d[b] d[b]d[b] ^ d[c]=x d[c]=xd[c]=x的式子,那我們嘗試將插入順序改變,變成:d[a]、d[b]、x、d[c]。那麼顯然,d[c]是不可能插入成功的,簡單的證明:
       d[a]^ d[b] ^ d[c]=x
     d[a]^ d[b]^ x=d[c] (根據上面那條並沒有什麼卵用的異或性質)
       原來是x插入不進去,改變順序後,d[c] d[c]d[c]插入不進去,也就是說,對於插入不進去的元素,改變插入順序後,要麼還是插入不進去,要麼就是插入進去了,同時另一個原來插入的進去的元素插入不進去了,所以,可以插入進去的元素數量一定是固定的。
       顯然,如果你去掉線性基裏面的任意一個數,都會使得原序列裏的一些(或一個)數無法通過用線性基裏的元素異或得到,所以,每一個元素都是必要的,換句話說,這裏面沒有多餘的元素,所以,這個線性基的元素個數在保持性質1的前提下,一定是最少的。
       順便貼上插入的代碼(代碼裏的ll 都是指longlong):

void add(ll x)
{
    for(int i=50;i>=0;i--)
    {
        if(x&(1ll<<i))//注意,如果i大於31,前面的1的後面一定要加ll
        {
            if(d[i])x^=d[i];
            else
            {
                d[i]=x;
                break;//記得如果插入成功一定要退出
            }
        }
    }
}


如何求最大值


       完整的說,是如何求在一個序列中,取若干個數,使得它們的異或和最大。
       首先構造出這個序列的線性基,然後從線性基的最高位開始,假如當前的答案異或線性基的這個元素可以變得更大,那麼就異或它,答案的初值爲0。
       代碼如下:

ll ans()
{
    ll anss=0;
    for(int i=50;i>=0;i--)//記得從線性基的最高位開始
    if((anss^d[i])>anss)anss^=d[i];
    return anss;
 }   


如何求最小值


       顯然的,最小值一定是最小的d[i]。(如果讓最小的d[i]去異或其它的d[i]一定會讓它變得更大,所以它自己就是最小的)
 

如何求第k小的值


       完整的說,應該是——從一個序列中取任意個元素進行異或,求能異或出的所有數字中第k小的那個。
       首先,要對這個序列的線性基處理一下,對於每一個d[i] d[i]d[i],枚舉j=i to1,如果d[i] (2)的第j位爲1,那麼d[i]異或d[j−1]。
       那麼處理完一個線性基之後,應該大致是長這個樣子的(x表示0或1):
       1xxxx0xxx0x
                1xxx0x
                        1x
       求解過程:將k先轉成二進制,假如k的第i位爲1,ans就異或上線性基中第i ii個元素(注意不是直接異或d[i-1])。
       代碼如下:

void work()//處理線性基
{
	for(int i=1;i<=60;i++)
	for(int j=1;j<=i;j++)
	if(d[i]&(1<<(j-1)))d[i]^=d[j-1];
}
ll k_th(ll k)
{
	if(k==1&&tot<n)return 0;//特判一下,假如k=1,並且原來的序列可以異或出0,就要返回0,tot表示線性基中的元素個數,n表示序列長度
	if(tot<n)k--;//類似上面,去掉0的情況,因爲線性基中只能異或出不爲0的解
	work();
	ll ans=0;
	for(int i=0;i<=60;i++)
	if(d[i]!=0)
	{
		if(k%2==1)ans^=d[i];
		k/=2;
	}
}

如何判斷一個數是否能被當前線性基中的元素異或得到

       把它嘗試插入進線性基裏面去,假如可以插入,說明不能異或得到,假如插不進去,說明不能異或得到 (原理然而上面已經講了)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章