Luogu 3292 SCOI2016 幸運數字

2022-06-13 04:09:11 字數 1790 閱讀 5279

bzoj 4568。

感覺很板。

前置技能:線性基。      放一篇感覺講的比較豐富的部落格: 戳這裡。

首先要求在乙個序列中任意選點使得異或和最大,當然是想到線性基了。

把問題轉換到樹上,如果每次詢問的序列是兩點之間的路徑,也就是說我們只要提取出樹上一條路徑的線性基就可以了吧。

發現線性基滿足可以快速合併這個性質,如果要合併的話只要把乙個暴力插到另乙個裡面去就行了,這樣是兩個$log$,我們還可以啟發式合併,把小的插到大的裡面去,這樣會更快。

所以我們發現可以鏈剖或者倍增來維護這個東西,我這麼懶,當然是倍增了。

注意倍增的時候是點形成的集合而不是邊形成的集合。

再提兩句:

1、線性基並不滿足區間可減性,所以大力可持久化應該是不行的。

2、點分治可以減少乙個$log$,再用$tarjan$求一求$lca$會更快。

時間複雜度$o(nlog^3n)$。

code:

#include #include 

using

namespace

std;

typedef

long

long

ll;const

int n = 20005

;const

int b = 62

;const

int lg = 16

;int n, qn, tot = 0

, head[n], dep[n], fa[n][lg];

ll a[n];

struct

edge e[n

<< 1

];inline

void add(int

from, int

to)

template

inline

void read(t &x)

struct

lp

inline

void

ins(ll val)

val ^=p[i];}}

}inline ll getmax()

} s[n][lg];

inline lp merge(lp u, lp v)

} else

}return

res;

}inline

void swap(int &x, int &y)

void dfs(int x, int fat, int

depth)

for(int i = head[x]; i; i =e[i].nxt)

}inline lp getlp(

int x, int

y)

if(x == y) return

res;

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

if(fa[x][i] !=fa[y][i])

res = merge(res, s[x][0]), res = merge(res, s[y][0

]);

return

res;

}inline

void solve(int x, int

y) int

main()

dfs(

1, 0, 1

);

for(int x, y; qn--; )

return0;

}

view code

唔,linear basis居然被我寫成了lp……無話可說

luogu3292 幸運數字

考慮點分治,將詢問離線後計算重心到每乙個點的線性基,然後再詢問重心到每乙個點的線性基,時間複雜度為 o 3600q 可以過 然而太菜的我寫了倍增維護線性基,震驚於倍增和線性基常數之小 1 include2 using namespace std 3 define n 20005 4 define o...

P3292 SCOI2016 幸運數字

lca倍增的途中,merge一下線性基。注意線性基是從自己開始,而倍增陣列fa i 0 等於father,意味著,線性基倍增的區間是左閉右開的,fa陣列是左閉右閉 include include include include include include include include incl...

P3292 SCOI2016 幸運數字 題解

這道題的解法是倍增 lca 線性基。下面假設你已經學會了倍增 lca 和線性基。首先回顧倍增 lca 的過程 通過倍增合併 f 到 f 上。實際上線性基也是可以合併的,合併方式就是將乙個線性基插入到另外乙個線性基裡面。因此既然線性基可以合併,那麼根據倍增 lca 的思路,我們同樣可以倍增合併線性基。...