bzoj 4568。
感覺很板。
前置技能:線性基。 放一篇感覺講的比較豐富的部落格: 戳這裡。
首先要求在乙個序列中任意選點使得異或和最大,當然是想到線性基了。
把問題轉換到樹上,如果每次詢問的序列是兩點之間的路徑,也就是說我們只要提取出樹上一條路徑的線性基就可以了吧。
發現線性基滿足可以快速合併這個性質,如果要合併的話只要把乙個暴力插到另乙個裡面去就行了,這樣是兩個$log$,我們還可以啟發式合併,把小的插到大的裡面去,這樣會更快。
所以我們發現可以鏈剖或者倍增來維護這個東西,我這麼懶,當然是倍增了。
注意倍增的時候是點形成的集合而不是邊形成的集合。
再提兩句:
1、線性基並不滿足區間可減性,所以大力可持久化應該是不行的。
2、點分治可以減少乙個$log$,再用$tarjan$求一求$lca$會更快。
時間複雜度$o(nlog^3n)$。
code:
#include #includeview codeusing
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;
}
唔,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 的思路,我們同樣可以倍增合併線性基。...