模板 數學 線性基

2022-05-07 23:06:19 字數 4071 閱讀 8118

#includeusing

namespace

std;

#define ll long long

const

int mn=60

;ll a[mn],tmp[mn];

bool flag;//

該線性基能否表示0

//嘗試向線性基中插入乙個值

void

ins(ll x)

else

x^=a[i];

flag=true;}

//判斷該線性基能否表示x

bool

check(ll x)

//查詢該線性基能表示的最大值

ll qmax(ll res=0

) //

查詢線性基能表示的最小值

ll qmin()

//查詢線性基能表示的第k小值

ll query(ll k)

if(k>=(1ll

;

for(int i=0; i)

if(k&(1ll

return

res;}//

把線性基s1和s2合併,合併的結果放入res中

void merge(ll *res,ll *s1, ll*s2)

intmain()

終於開始啃這個破東西了。

抄襲自:

給定n個整數(數字可能重複),求在這些數中選取任意個,使得他們的異或和最大。

線性基是一種擅長處理異或問題的資料結構.設值域為 $[1,n]$,就可以用乙個長度為 $\lceil \log_2n \rceil$ 的陣列來描述乙個線性基。特別地,線性基第 $i$ 位上的數二進位制下最高位也為第 $i$ 位。

乙個線性基滿足,對於它所表示的所有數的集合 $s$ , $s$ 中任意多個數異或所得的結果均能表示為線性基中的元素互相異或的結果。

即,線性基能使用異或運算來表示原數集使用異或運算能表示的所有數。運用這個性質,我們可以極大地縮小異或操作所需的查詢次數。

我們考慮插入的操作,令插入的數為 $x$ ,考慮 $x$ 的二進位制最高位 $i$ ,

若線性基的第 $i$ 位為0,則直接在該位插入 $x$ ,退出;

若線性基的第 $i$ 位已經有值 $a_i$ ​,則 $x = x \oplus a_i $ ,重複以上操作直到 $x=0$ 。

如果退出時 $x=0$ ,則此時線性基已經可以表示原先的 $x$ 了;反之,則說明為了表示 $x$ ,往線性基中加入了乙個新元素。

很容易證明這樣複雜度為 $\log_2x$ ,也可以用這種方法判斷能否通過原數列異或得到乙個數 $x$ 。

查詢最小值相對比較簡單。考慮插入的過程,因為每一次跳轉操作, $x$ 的二進位制最高位必定單調降低,所以不可能插入兩個二進位制最高位相同的數。而此時,線性基中最小值異或上其他數,必定會增大。所以,直接輸出線性基中的最小值即可。

考慮異或最大值,從高到低遍歷線性基,考慮到第 $i$ 位時,如果當前的答案 $x$ 第 $i$ 位為0,就將 $x$ 異或上 $a_i$ ​;否則不做任何操作。顯然,每次操作後答案不會變劣,最終的 $x$ 即為答案。

同樣,我們考慮對於乙個數 $x$ ,它與原數列中的數異或的最值如何獲得。用與序列異或最大值類似的貪心即可解決。

我們考慮進一步簡化線性基。顯然,乙個線性基肯定可以表示為若干個形如 $2^i$ 的數。從高到低處理線性基每一位,對於每一位向後掃,如果當前數第 $i$ 位為0,且線性基第 $i$ 位不為0,則將當前數異或上 $a_i$ 。這一操作可以在 $o(n^2)$ 的時間內解決。

經過這一步操作後,設線性基內共有 $cnt$ 個數,則它們共可以表示出 $2^$ 個數。當然,對於0必須特殊考慮。如果插入的總數 $n$ 與 $cnt$ 相等,就無法表示0了。

同樣,考慮最小值時,也必須要考慮到0的情況。事實上,如果插入時出現了未被加入的元素,就肯定可以表示出0。

隨後,我們考慮將 $k$ 二進位制拆分,用與快速冪類似的方法就可以求出第 $k$ 小值。

學過線性代數的同學應該可以看出,這個過程就是對乙個矩陣求解異或意義下的秩的過程。因此, $cnt \leq \lceil \log_2n \rceil$ 一定成立。而最終,線性基中儲存的也是異或意義下的一組極小線性無關組。

同樣,有關線性基的一切運算都可以看做矩陣的初等行列變換,也就可以將其看做線性規劃問題。同樣,可以離線使用高斯消元來構造極小線性基。

1.設線性基的異或集合中不存在0。

2.線性基的異或集合中每個元素的異或方案唯一,其實這個跟性質1是等價的。

3.線性基二進位制最高位互不相同。

4.如果線性基是滿的,它的異或集合為[1,2n−1]。

5.線性基中元素互相異或,異或集合不變。

#includeusing

namespace

std;

#define ll long long

const

int mn=60

;ll a[

61],tmp[61

];bool flag;//

能否表示0

//嘗試向線性基中插入乙個值

void

ins(ll x)

else

x^=a[i];

flag=true;}

//判斷當前值能否被線性基表示

bool

check(ll x)

//查詢線性基能表示的最大值

ll qmax(ll res=0

) //

查詢線性基能表示的最小值

ll qmin()

//查詢線性基能表示的第k小值

ll query(ll k)

if(k>=(1ll

;

for(int i=0; i)

if(k&(1ll

return

res;

}int

main()

#includeusing

namespace

std;

#define ll long long

const

int mn=60

;ll a[

61],tmp[61

];bool flag;//

能否表示0

//嘗試向線性基中插入乙個值

void

ins(ll x)

else

x^=a[i];

flag=true;}

//判斷當前值能否被線性基表示

bool

check(ll x)

//查詢線性基能表示的最大值

ll qmax(ll res=0

) //

查詢線性基能表示的最小值

ll qmin()

//查詢線性基能表示的第k小值

ll query(ll k)

if(k>=(1ll

;

for(int i=0; i)

if(k&(1ll

return

res;

}int

main()

}if(check(x)==0

) }

printf(

"%lld\n

",(1ll<2008

);

return0;

}

有多少個線性基,就有多少2的多少次方種表示(若我們的線性基可以表示0,則包括0)。

不是很懂,感覺ins函式中途返回的話就是插入乙個新的值,到最後才返回的話說明這個數可以被表示。

先找到樹根,從樹根開始遍歷每個點,類似lca的處理,記錄他們每個點的高度為2的i次方的祖先以及到這個祖先的線性基最大值。最後是不是直接兩個點查lca然後找出他們到lca的線性基最大值直接異或就可以了?好像錯了,不能直接最大值異或。

記錄每個結點到根的線性基,然後查詢得時候求出lca合併線性基?

補乙個合併線性基的**:(假如某個線性基的一項不能被另乙個線性基表示,則插入這一項)

void merge(ll *d,ll *s1, ll*s2)

數學 線性基

這是乙個看起來很像gauss jordan消元法裡面的形式 行最簡形矩陣 的線性基,和其他人的gauss消元法裡面的形式 上三角形矩陣 看起來並不一樣。d i 表示掌管二進位制第i位 1ll 0 i n d i x for int j 0 j i j for int j i 1 j len j re...

模板 線性基模板

數學太差,直接線性基當資料結構用orz 表示數集 1,2 k 1 表示乙個異或集合 可以說是將原數集壓縮 性質 直接抄的orz,雖然也不是特別懂 0 2.線性基的異或集合中每個元素的異或方案唯一,其實這個跟性質1是等價的。3.線性基二進位制最高位互不相同。1,2n 1 1,2n 1 1,2n 1 5...

模板 線性基

難度較大,請勿棄療 給定n個整數 數字可能重複 求在這些數中選取任意個,使得他們的異或和最大。n 50sample input33 21sample output 3看上去莫名其妙地想貪心。給些定義 s 為無符號整數集 即s n 記為 xor sum s x or s um s s1 s2 s s ...