我現在大概理解線性基了。
假設你有乙個集合 \(s\),那你可以把它壓成乙個線性基 \(p\),然後使得兩個集合能異或出來的玩意完全一致。
我們是要按位操作。
假設你 \(s = \\),那你發現你的 \(p = \\) 是可以的。
然後 \(\\) 也是可以的。你要明白線性基這玩意不唯一,而且他是不同順序可能會有多種不同的線性基。
說幾個有用的性質先。
我們想象一下線性基的過程。線性基一共有 \(\log\) 位對不對。
我們考慮如果 \(x\) 的最高位為 \(2^i\) ,如果 \(p_i\) 是 0,也就是空的基的話。
我們就直接把 \(p_i = x\),這樣丟進去就好了/cy。
那麼如果 \(p_i != 0\) 怎麼辦,我們考慮到,如果 \(p_i\) 是插進去的話,那麼他也是原來的元素之一,所以和他異或一下還能是原集合能異或出來的數。
但是如果他是間接插進去的怎麼辦,它異或過其他數字!不用慌,轉化一下,由於那個位置是若干個原集合 \(s\) 的元素異或得來,當你插入這個元素,和原集合的元素異或的時候,它還是原集合能異或出來的數字對不對。
所以你整個集合都是原集合能異或出來的數字構成的,在特殊情況下,它其實就是原集合的數構成的,當你順序插入的時候。
所以先異或上,然後你的最高位就沒掉了,接著向下迴圈,直到能插進去位置,如果他插不進去?為什麼呢,因為它會變成 \(0\)。
因為你每一位都是可以控制的,控制的意思大概是指,你的 \(i\) 位如果有元素,那麼你可以選擇這一位 \(2^i\) 是選還是不選,記住,此時你只關心這個最高位。也就是從高位貪心。
如何證明線性基的元素都能異或出來原集合的元素呢?
我們再考慮,如果你丟進去 \(\\),你發現你只能異或出來 \(\) 對吧。
你發現如果你丟到線性基里,你的線性基元素也是 \(\\),還是只能構造出來這麼多。
但是如果你原集合是 \(\\) 呢?你考慮到,你丟進去6,插入成功了,線性基 \(\\)
然後再插入10,顯然能丟進去對吧,線性基 \(\\),然後我們發現原集合能構造出來是 \(\\),而你線性基構造出來的還是 \(\\)。
假如,我們要求乙個數字,能不能被乙個這個集合表示,怎麼辦,我們就假裝插入(霧)
按照插入的姿勢,直接丟進去,看有沒有乙個位置為空,如果有乙個位置為空的,直接return 1就好了,否則就return 0
我們發現 \(p_i\) 的最高位是 \(2^i\) 對吧,且其他元素的最高位都不和這個重複。
我們倒著取(這裡指倒著迴圈),如果 \(p_i\) 這一位存在,我們自然是不取這乙個基的,否則就取過來,因為你乙個 \(2^i \geq p_\),易證,不證明了。
然後你可以有效的控制 \(2^i\) 這一位到底有沒有,因為你 \(p_i\) 一定存在 \(i\) 這一位,但是你 \(p_j [j \geq i]\) 也可能存在 \(i\) 這一位所以不能直接取,要取乙個 \(max(ans , ans \oplus p_i)\)。
如果我們想求 \(k\) 大 \(k\) 小的話,那麼我們要去關心 \(i\) 必須不能被比他大的元素干擾,所以我們要消除這個貢獻也就是 \(p_i \& (2^j) [j < i]\) 的時候 \(p_i \oplus p_j\)
loj 有個題可以去試試
**長這個樣子
p3812 【模板】線性基
// by isaunoya
#include using namespace std;
#define rep(i, x, y) for (register int i = (x); i <= (y); ++i)
#define rep(i, x, y) for (register int i = (x); i >= (y); --i)
#define int long long
const int _ = 1 << 21;
struct i
inline i& operator>>(int& x)
inline i& operator>>(double& x)
x = sign ? x : -x;
return *this;
} inline i& operator>>(char& x)
inline i& operator>>(string& s)
} in;
struct o
inline o& operator<<(int x)
inline o& operator<<(char x)
inline o& operator<<(string s)
} out;
#define pb emplace_back
#define fir first
#define sec second
int n, a[64], p[64];
signed main()
}} int ans = 0;
for (int i = 50; ~i; --i) ans = max(ans, ans ^ p[i]);
out << ans << '\n';
return out.flush(), 0;
}
p4151 [wc2011]最大xor和路徑
找個出所有簡單環,這樣就可以和原有路徑抵消掉
最後求異或,沒了
// by isaunoya
#include using namespace std;
#define rep(i, x, y) for (register int i = (x); i <= (y); ++i)
#define rep(i, x, y) for (register int i = (x); i >= (y); --i)
#define int long long
const int _ = 1 << 21;
struct i
inline i& operator>>(int& x)
inline i& operator>>(double& x)
x = sign ? x : -x;
return *this;
} inline i& operator>>(char& x)
inline i& operator>>(string& s)
} in;
struct o
inline o& operator<<(int x)
inline o& operator<<(char x)
inline o& operator<<(string s)
} out;
#define pb emplace_back
#define fir first
#define sec second
template inline void cmax(t& x, const t& y)
template inline void cmin(t& x, const t& y)
int n, m;
const int n = 5e4 + 10;
const int m = 1e5 + 10;
int cnt = 0, head[n];
struct edge e[m << 1];
int d[n], p[65];
bool vis[n];
void add(int u, int v, int w) , head[u] = cnt;
e[++cnt] = , head[v] = cnt;
}void insert(int val)
}void dfs(int u, int cur)
}int query(int val)
signed main()
dfs(1, 0);
out << query(d[n]) << '\n';
return out.flush(), 0;
}
留一些坑。
洛谷p3857 [tjoi2008]彩燈
洛谷p4301 [cqoi2013]新nim遊戲
cf895c square subsets
洛谷p4570 [bjwc2011]元素
洛谷p3265 [jloi2015]裝備購買
洛谷p3292 [scoi2016]幸運數字
洛谷p4151 [wc2011]最大xor和路徑
cf724g xor-matic number of the graph
cf938g shortest path queries
線性基學習筆記
線性基是幹嘛的呢?給定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...