線性基學習筆記


對於一個 \(m\) 維向量組,每一個向量表示為形如 \((x_1,x_2,...,x_m)\)
如果存在一個向量可以用其他向量表示出來,稱為線性相關
否則,稱為線性無關
所有向量組可以形成的向量集合稱為線性空間
求出向量組的一個線性無關的子集,其可以組成的線性空間不變,稱為線性空間的一組基

對於一個向量組,對於其基的求解可以用高斯消元來實現
證明高斯消元的操作對線性空間的大小沒有影響:

  • 交換兩行:顯然沒有影響
  • 加上另一行的數倍:相當於加上另一個向量,那么這個向量本身可以用新形成的向量表示出來,也沒有影響

於是通過高斯消元求出最大的基底

雖然在 OI 中實數的基底很不常見,但是這是基底的本質

比如這道題可以作為模板:P3265 [JLOI2015]裝備購買

題目中線性無關的限制太明顯了,提示需要構建基底
這道題里由於有了價格的限制,可以先排個序,再把高斯消元的過程動態進行
具體來說是這樣的:從大到小枚舉每一位,如果某一維還沒有基,那么可以直接把這個向量作為那一維的基
否則,將這一維和這一維的基加減抵消成零

代碼實現
#include<bits/stdc++.h>
using namespace std;
const int maxn=505;
int n,m,ans,ans1,b[maxn];
double eps=1e-5;
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct Node{
	double a[maxn];
	int val;
}p[maxn];
bool operator < (Node a,Node b){
	return a.val<b.val;
}
double ffabs(double x){
	return x<0?-x:x;
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>p[i].a[j];
	for(int i=1;i<=n;i++)p[i].val=read();
	sort(p+1,p+n+1);
	for(int i=1;i<=n;i++){
		for(int j=m;j>=1;j--){
			if(ffabs(p[i].a[j])<eps)continue;
			if(!b[j]){
				b[j]=i;ans+=p[i].val;ans1++;
				break;
			}
			double chu=p[i].a[j]/p[b[j]].a[j];
			for(int k=j;k>=1;k--){
				p[i].a[k]-=chu*p[b[j]].a[k];
			}
		}
	}
	cout<<ans1<<" "<<ans;
	return 0;
}

在 OI 中線性基幾乎特指在異或中的應用
模板題為例,要求最大異或子集
可以模仿構建基底的過程,從高到低確定每一位,根據二進制的性質,這樣一定是最優的
根據基底的本質來理解,如果構建出線性基,相當於可以異或出原來的數能異或得到的所有數
那么答案直接在線性基上貪心選取每一位即可

  • 最后注意一點:大於的優先級是高於異或的哦~
代碼實現
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,x,a[100],ans;
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++){
		x=read();
		for(int j=50;j>=0;j--){
			if((x>>j)&1){
				if(a[j])x^=a[j];
				else{
					a[j]=x;
					break;
				}
			}
		}
	}
	for(int i=50;i>=0;i--)if(!((ans>>i)&1))ans^=a[i];
	cout<<ans;
	return 0;
}

P4570 [BJWC2011]元素

和上面一樣的套路,先排序,線性基用作判斷能否加入


P4301 [CQOI2013] 新Nim游戲

為了讓剩余火柴的子集不為零,構建線性基,排序后能插入則插入,否則拿走


P3857 [TJOI2008]彩燈

用到線性基的結論:若基中值有 \(cnt\) 個,那么可以拼湊出的個數為 \(2^{cnt}\)


P4869 albus就是要第一個出場

這道題的不同之處在於不用去重,那么要用到另一個結論,每一個能拼湊的數的拼湊方案數是 \(2^{n-cnt}\),即隨便一個基外的子集都可以添加進來


P4151 [WC2011]最大XOR和路徑

這就要用到線性基維護圖上問題的新科技了

可以發現由於路徑的可重,那么最終的路徑的一定是一條簡單路徑外加許多環(因為環相當於是一去一回,而重疊部分相互抵消)

於是把所有還放進線性基即可,由於環的個數很多,但是不同環之間可以由異或得出,所以只放返祖邊形成的環即可

另外簡單路徑是可以隨意選的,因為和其他路徑可以通過異或環得出


CF724G Xor-matic Number of the Graph

好的,現在是前面兩天道題的結合版,首先一樣的把所有環放進線性基里。

每一位的貢獻分開考慮
若有環這一位為 \(1\),那么任意兩點異或這個環便可以加上這位貢獻的 \(1\)
方案數為 \(2^{|S|-1}\binom{n}{2}\)

若沒有環這一位為 \(1\),那么只有路徑這一位為 \(1\) 才能產生貢獻
方案數為 \(2^{|S|}cnt(n-cnt)\),其中 \(cnt\) 表示距離這一位為 \(1\) 的點的個數

注意圖可能不聯通


接下來就是線性基的合並了,由於線性基是 \(log\) 位的,那么直接暴力合並 \(log^2\) 即可


P4839 P哥的桶

操作用線段樹都能維護,直接上即可


P5607 [Ynoi2013] 無力回天 NOI2017

發現這次不能維護了,因為修改變成了區間修改
考慮將區間修改變成單點修改,那么差分即可
但是這樣就需要發現差分數組與原數組線性基的關系了
發現原數組展開后差分數組 \(b_{[1,l]}\) 的部分是重疊的,那么可以發現原數組的線性基於 \(a_l\) 加上 \(b_{[l+1,r]}\) 的線性基是等價的

那么用線段樹維護差分數組的線性基,用樹狀數組動態維護原數組的值即可


發現直接合並的復雜度實在太暴躁了,在有些題中不足以通過,那么需要再加入一些小 \(trick\)


CF1100F Ivan and Burgers

考慮離線回答
可以按照右端點排序,只要線性基中的數在左端點右側即可使用
那么每次有沖突時可以貪心地選擇位置靠右的


P3292 [SCOI2016]幸運數字

一樣的套路,維護每個點到根的線性基,深度越深越好,查詢時深度大於 \(lca\) 即可使用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM