演算法筆記 線性基詳解

2022-05-23 09:33:09 字數 3073 閱讀 5373

線性基是乙個聽起來很高階但實際上還蠻簡單的演算法,本質就類似於線性代數中的基向量,是乙個整數集合的一組線性無關的數字。說是演算法,我覺得其更偏向是一種思想。

例如一組向量:

v1=v2=

v2=三者中任意乙個都無法用另外兩者表示出來,因此它們線性無關。

舉個線性基的例子(以下為二進位制表示):

對乙個整數集合求線性基得到

(這也是一組數不是向量啊

線性基三者中任意乙個都無法通過另外兩者異或得到,且原集合中的任意乙個數都可以通過線性基中的某些數異或得到。

求線性基的過程如下:

ll lb[61];//線性基,大小取決於數字值域

int inse(ll x)//將x插入線性基,插入成功返回1,否則返回0

for (ll i=1; i<=n; i++) inse(a[i]);

for (int i=60; i>=0; i--)

if (lb[i]) res=res*2%2008;

cout實數情況的線性基,用類似高斯消元的方法操作一下,然後過程中貪心的選**最小的。

ps:注意eps的設定要大一點,否則由於計算精度不太夠,判0太嚴格會wa掉

#include #define inf 0x3f3f3f3f

using namespace std;

typedef long long ll;

const long double eps=1e-5;

int n, m, vis[510], cost[510], ans_, ans;

long double v[510][510];

int main()

a[maxn];

ll n, lb[100], ans;

ll check(ll x)

void ins(ll x)

); for (ll i=1; i<=n; i++)

if (check(a[i].id)) ans+=a[i].v, ins(a[i].id);

cout本質跟上面那題基本一樣。

根據博弈論sg函式的知識,我們知道要讓先手保證獲勝,就要保證兩個人都執行完特殊操作後,剩下的火柴堆異或起來不為0。也就是要保證先手進行完特殊操作後,剩下的火柴堆沒有異或為零的組合(否則後手者只要把組合之外的火柴都拿掉就好了)

所以要讓先手必勝,我們只要把會導致 (某些火柴堆異或為0 )的那些火柴堆拿掉就好了。為了讓拿走的火柴數最小,先按火柴數降序排列,然後逐個嘗試插入線性基,把插入失敗的火柴堆取走就行了。

#include #define maxn 50100

using namespace std;

typedef long long ll;

ll n, lb[100], num[maxn], ans;

int ins(ll x)

); for (int i=1; i<=n; i++)

if (!ins(num[i])) ans+=num[i];

cout有點巧妙的題目。

每條邊和點可以走多次,稍加觀察可以發現,假設我們本來是沿著某一路徑從1走到n,在中途其實可以走其它邊,然後走到某乙個環,轉完那個環再原路返回,此時答案只異或上了那個環上的所有值,前往那個環的路上所有值都被異或了兩次所以等於沒異或。

因此做法就是,找到一條1到n的路徑,答案就是這條路徑上的值異或起來加入線性基,再把所有環的異或加入線性基,即可構造出最大的答案。

1到n的路徑可能有多個,其實隨便用哪乙個都一樣的。因為如果存在多個1到n的路徑,它們本身也是成環的,在進行線性基的過程中會自然被替換成最優的選擇。

還有乙個問題:要找出所有環的話複雜度似乎至少得n*m?反正肯定會超時。其實只要隨便從乙個點用dfs找環就行了,雖然找的不全,但神奇的是它確實不影響答案的正確性。(個人認為是因為沒找到的環其實都是可以通過找到的環異或得到

#pragma gcc optimize(2)

#include #define maxn 50100

using namespace std;

typedef long long ll;

const ll mod=998244353;

vector> edge[maxn];

ll n, m, lb[100], res, vis[maxn], buf[maxn];

void add(ll x)

dfs(1, 0, 0);

for (ll i=61; i>=0; i--)

if (!(res&(1ll這幾題裡面唯一一道**長一點的題。據說可以用點分治搞成o(nlog2n),但是點分治不怎麼熟就還沒研究那個做法。

每次要對一條路徑上的所有值求線性基。可以把這條路徑分成兩半,u->lca(u, v)和v->lca(u,v),可以對兩段分別求線性基再合併起來。求乙個點到其某個祖先的線性基可以利用倍增的思想,對不超過logn個線性基進行合併得到。

整體來說非常像倍增求lca(我就是按照那個板子改的),複雜度單次合併是o(log2n)的,每次詢問合併logn次,所以複雜度應該是o(mlog3n)

#pragma gcc optimize(2)

#include #define maxn 20100

using namespace std;

typedef long long ll;

const ll mod=998244353;

const long double eps=1e-5;

int n, q, lg[maxn], fa[maxn][20], deep[maxn];

ll lb[maxn][20][61], buf1[61], buf2[61], num[maxn];

vectoredge[maxn];

void inse(ll x, ll *res)

merg(buf1, buf2, buf1);

inse(num[fa[x][0]], buf1);

inse(num[x], buf1);

inse(num[y], buf1);

return fa[x][0];

}int main()

dfs(1, 0);

for (ll i=1, u, v; i<=q; i++)

}

線性基學習筆記

線性基是幹嘛的呢?給定n個數,求所有數的異或和最大是多少?求解這類問題的時候,就需要線性基了 個人感覺線性基本身就一種貪心。首先定義ba se i bas e i 表示最高位1在i位的數是什麼 對於新進來的數tm p tmp 我們先找出他最高位上的1,假設為第 j j 位,然後看一下ba se j ...

線性基 學習筆記

includeusing namespace std using ll long long const int maxn 5e5 5 原來的數 const int maxbit 63 ll a maxn 原來的數 ll p maxbit p j 第j位為最高位1的數 最高位1在第j位的數 int m...

線性基 學習筆記

按位計算,如果相同記為0,不同記為1。如果,a b c,c b a 交換律結合律 對於任何數,x x 0,x 0 x 對 於一 段序列a n,異或 和為a1 a2 an 對於一段序列a n,異或和為a 1 a 2 a n 對於一段序列 an 異或和為 a1 a2 an 設t s,所有 這樣的子 集t...