好久沒寫部落格了,最近學習了一下新姿勢:線性基,這個就很厲害了。
線性基其實很好理解。
會線性基可以直接跳過下面的部分。。。
我們先丟擲一道簡單的題:
給你乙個長度為n的序列,在其中任選數字求可以得到的最大異或和。
我們知道任意兩個,和連續一段的最大異或和,可以用trie加貪心水掉,但這道題怎麼做呢?
線性基就可以用來處理這個玩意。
通俗的講:線性基其實是一些數的集合,而用這些集合裡的數字任意組合異或可以表示完原數列中所有數字異或的情況。
而線性基中的數字有個重要性質就是 不會存在有兩個數他們的二進位制最高位相同。
所以線性基集合大小最多不會超過原數列中最大數的二進位制位數。這個是顯然的。
這是**:其中greed陣列就表示了線性基集合。
inline
void greed_insert(ll x)
else x ^= greed[i];
}}
我們再來講講它為什麼可以表示完原來序列的所有情況。
假如我們要加入b,它的最高位是i,原來在這一位上已經存在乙個數a了,那麼我們令c=b^a,如果我們把c加進去c^a就可以表示出原來的b了所以我們加入c進去不會缺失所有異或的情況(也就是說可以湊出a,b,a^b)。
是不是非常easy。
讓我們回到這道題上面:
這道題題意其實就是:我們把原來序列上的問題轉換到了樹上來做,現在每次詢問問你u到v路徑上的最大異或和。
其實做法差不多,只需要加乙個st表,每次詢問暴力合併線性基就可以了(乙個乙個插進去)。
ac**,可能有點醜。
#include
#include
#include
#include
#include
using
namespace
std;
#define n 20005
#define ll long long
#ifdef win32
#define auto "%i64d"
#else
#define auto "%lld"
#endif
struct edge ;
edge e[2 * n];
int n, m, fa[n][20], a, b, num, head[n], dep[n];
ll aa[n], xor[n][20][62];
inline
void adde(int i, int j)
inline
void insert(ll x, int u, int k)
else x ^= xor[u][k][i];
}}inline
void dfs(int u)
}ll greed[62];
inline
void greed_insert(ll x)
else x ^= greed[i];
}}ll _greed()
ll solv(int u, int v)
}if( u == v ) return _greed();
for(int j = 18; j >= 0; j--)
for(int k = 0; k <= 61; k++)
greed_insert(xor[u][0][k]),
greed_insert(xor[v][0][k]);
return _greed();
}int main()
fa[1][0] = 1;
dfs(1);
for(int j = 1; j <= 18; j++) }}
while(m--)
return
0;}
bzoj4568 樹上倍增 線性基 幸運數字
description a 國共有 n 座城市,這些城市由 n 1 條道路相連,使得任意兩座城市可以互達,且路徑唯一。每座城市都有乙個 幸運數字,以紀念碑的形式矗立在這座城市的正中心,作為城市的象徵。一些旅行者希望遊覽 a 國。旅行者計畫 乘飛機降落在 x 號城市,沿著 x 號城市到 y 號城市之間...
BZOJ4568 線性基 樹鏈剖分 線段樹
a 國共有 n 座城市,這些城市由 n 1 條道路相連,使得任意兩座城市可以互達,且路徑唯一。每座城市都有乙個幸運數字,以紀念碑的形式矗立在這座城市的正中心,作為城市的象徵。一些旅行者希望遊覽 a 國。旅行者計畫乘飛機降落在 x 號城市,沿著 x 號城市到 y 號城市之間那條唯一的路徑遊覽,最終從 ...
BZOJ4568 Scoi2016 幸運數字
樹上查兩點間最大異或和 樹倍增,每個點維護向上2 k個點的線性基,然後在查lca的時候合併 關於點權維護倍增略蛋疼 合併線性基的時候就直接把乙個線性基里的插到另乙個裡 複雜度o m log n log 2inf 合併的時候加點優化可以降掉乙個loginf 接下來bb一些有關線性基和最大異或和的東西 ...