1. 程式人生 > >學習筆記第二十三節:線性基

學習筆記第二十三節:線性基

正題

      注意!本篇文章注意區分插入填入

      這個神奇的資料結構在很早之前就會了,現在寫部落格回憶一下。

      線性基有著非常好的性質,它主要是用來解決異或和之類的問題。

      他是一個什麼樣子的東西呢?

      首先他是一個數組,是一個log_2(s)那麼大的陣列。它存些什麼呢?

      我們在這裡假設p_i就是線性基。

      p_i有兩種取值,第一種就是空;第二種是二進位制最高位為i的任意數的異或和。

      先等等,這樣直接說好像沒有什麼意義。

      不如我們先探求一下它的性質,再想著如何去維護?

      性質1:線性基中的數可以異或出原陣列。

      性質2:線性基第i個的二進位制最高位為i。

       接下來,我們來研究怎麼保證這兩個性質。

插入

      插入?

      假設插入的數是x,那麼我們找到x的二進位制最高位i,如果線性基的第i個數為空,那麼直接填進去。

      否則,我們就異或上線性基中的第i個數,那麼可以保證x的最高位肯定小於i了。

      繼續往下找。

      很明顯可以保證線性基的性質,如果最後沒有填進去,那麼說明線性基中的數已經可以組成x了。

程式碼<略醜>

void insert(long long*now,long long c){
	for(int i=60;i>=0;i--)
		if(c>>i){
			if(now[i]) c^=now[i];
			else {now[i]=c;break;}
		}
}

刪除

      不好意思線性基不支援刪除,因為具有後效性;遇到支援刪除的線性基,儘量倒過來構造(從後往前)。

查詢最大

      我們很明顯可以想到一種貪心,拿一個ans從線性基中的高位往低位掃,如果異或上當前的數可以使得答案變大,那麼我們就異或上它。否則就不異或。

      證明也是極其簡單的。(因為根據性質一,線性基中的數可以異或出原陣列,所以原陣列可以異或得到的,線性基也可以異或得到。

for(int q=60;q>=0;q--) ans=max(ans,ans^now[q]);

查詢最小

      分兩種情況討論。

      1.線性基中的數的個數等於原陣列中的數的個數,說明,每個數都填入了一個位置,輸出線性基中非空最低位的數即可。

      2.否則,一定有數沒有填入,說明,當前這個數可以被 未插入這個數的線性基組成,他們兩個異或起來等於0.最小為0.

查詢一個數是否可以被n個數異或得到

      首先n個數建一個線性基,看一下這個數能否填入線性基中,如果可以,那麼就不能被n個數異或得到,否則可以。

查詢第k小

      首先我們可以做一點事情使得線性基擁有美好的性質。

      我們儘量讓p_i這一位只有i這位為1。

      怎麼做到?我們尋找p_i二進位制第二高位的j,然後讓p_i \^\ =p_j,就可以消掉j這位,繼續往下消,直到消到最小。

      首先,線性基的正確性是顯然的。

      如果p_j為空呢?

      那麼我們至少保證了p_j為空。(沒問題啊。。。

      第k小,我們把k二進位制拆分,如果第i位為1,那麼就異或上線性基中非空的第i位。

      思考怎麼證明。第i個數如果非空,那麼肯定擁有唯一的第i位,因為上面的都被異或掉了,下面的本來就沒有。

      所以不管怎麼異或, 如果線性基中第i個數是非空的,肯定只有這個數控制著第i位,所以就相當於非空的線性基組成二進位制數一樣,異或起來就行了。

      注意判斷是否存在0對答案的影響

void prepare(now){
	for(int i=60;i>=0;i--)
		for(int j=i;j>=0;j--)
			if(now[i]>>j) now[i]^=now[j];
}

int find_kth(long long k){
	prepare(now);
	int t=0,d[65];//t記錄有多少個非空的數位,d[i]表示第i個非空數位是哪一個
	for(int i=0;i<=60;i--) 
		if(now[i]) d[t]=i,t++;
	if(t<n) k--;//非空數位小於n,那麼前面多了一個0,k減一
	if(k==0) return 0;//求的就是0
	long long ans=0;
	for(int i=t-1;i>=0;i--)//d的範圍是[0,t)
		if(k>>i){//第i位為1,那麼就異或上有數的第i位
			ans^=now[d[i]];
			k^=(1<<i);//消除對後面的影響
		}
	return ans;
}