1. 程式人生 > 實用技巧 >線性基彙總

線性基彙總

寫在前面

因為時間原因,就不寫學習筆記了(其實是因為懶)。只是簡單地把今天上午到現在刷的題彙總一下。
如果有想學習知識的同志還是建議先看一下這篇部落格—>線性基詳解

三條性質

  • 線性基的核心有三條性質,蒟蒻今天刷的題也基本上是這三條性質的應用

性質一:原序列裡面的任意一個數都可以由線性基裡面的一些數異或得到

主要應用:[WC2011]最大XOR和路徑
題解戳這裡


性質二:線性基裡面的任意一些數異或起來都不能得到 \(0\)

主要應用:[BJWC2011]元素

  • 貪心思想,排序優先選大的,用線性基判斷能不能選就行了
\(Code\)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 1010
#define ll long long
#define R register
using namespace std;
inline ll read(){
	ll x = 0,f = 1;
	char ch = getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n;
ll p[65],ans;
struct data{
	ll num;
	int mag;
	inline bool operator <(const data &a)const{
		return mag > a.mag;
	}
}a[N];
bool check(ll x){
	for(R int i = 62;i>=0;i--){
		if(!(x>>i))continue;
		if(!p[i]){
			p[i] = x;
			return 1;//說明可以被選
		}
		x ^= p[i];
	}
	return 0;//被消完了就不能被選
}
int main(){
	n = read();
	for(R int i = 1;i <= n;i++){
		a[i].num = read(),a[i].mag = read();
	}
	sort(a+1,a+1+n);//貪心,先選大的
	for(R int i = 1;i <= n;i++)	{
		if(check(a[i].num))ans+=a[i].mag;
	}
	printf("%lld\n",ans);
	return 0;
}

性質三:線性基裡面的數的個數唯一,並且在保持性質一的前提下,數的個數是最少的

主要應用:[TJOI2008]彩燈

  • 這題其實也用到了性質一,放在這裡是因為只有符合性質三的同時才可以將答案用線性基統計出來
  • 顯然開關相當於異或操作,講每一串燈控制的範圍轉化為 \(10\) 進位制數,放進線性基裡,此時線性基內的元素便可表示所有情況,每個元素都有選或不選的情況。故有 \(2^{cnt}\) 種情況,\(cnt\) 即為線性基內元素的個數,插入的時候統計下來就行了。
\(Code\)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 20010
#define ll long long
#define R register
using namespace std;
inline int read(){
	int x = 0,f = 1;
	char ch = getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int mod = 2008;
int n,m;
ll p[100],cnt;
char s[100];
void LineBase(ll x){
	for(R ll i = 55;i>=0;i--){
		if(!(x>>i))continue;
		if(!p[i]){
			p[i] = x;
			cnt++;
			break;
		}
		x ^= p[i];
	}
}
int main(){
	n = read(),m = read();
	for(R ll i = 1;i <= m;i++){
		scanf("%s",s+1);
		ll len = strlen(s+1);
		ll tmp = 0;
		for(R ll j = 1;j <= len;j++){
			tmp += (s[j]=='O') ? 1ll<<(j-1) : 0;
		}
		LineBase(tmp);
	}
	ll ans = pow(2ll,cnt);
	printf("%lld\n",ans%mod);
	return 0;
}

還有這道題也是很不錯的一道題,是用倍增將線性基快速合併
[SCOI2016]幸運數字
題解戳這裡