[SCOI2016]幸运数字
题目描述
A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一。每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征。
一些旅行者希望游览 A 国。旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y 城市起飞离开 A 国。在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上。然而,幸运是不能简单叠加的,这一点游览者也十分清楚。他们迷信着幸运数字是以异或的方式保留在自己身上的。
例如,游览者拍了 3 张照片,幸运值分别是 5,7,11,那么最终保留在自己身上的幸运值就是 9(5 xor 7 xor 11)。
有些聪明的游览者发现,只要选择性地进行拍照,便能获得更大的幸运值。例如在上述三个幸运值中,只选择 5 和 11 ,可以保留的幸运值为 14 。现在,一些游览者找到了聪明的你,希望你帮他们计算出在他们的行程安排中可以保留的最大幸运值是多少。
输入输出格式
输入格式:
第一行包含 2 个正整数 n ,q,分别表示城市的数量和旅行者数量。
第二行包含 n 个非负整数,其中第 i 个整数 Gi 表示 i 号城市的幸运值。
随后 n-1 行,每行包含两个正整数 x ,y,表示 x 号城市和 y 号城市之间有一条道路相连。
随后 q 行,每行包含两个正整数 x ,y,表示这名旅行者的旅行计划是从 x 号城市到 y 号城市。N<=20000,Q<=200000,Gi<=2^60
输出格式:
输出需要包含 q 行,每行包含 1 个非负整数,表示这名旅行者可以保留的最大幸运值。
输入输出样例
输入样例#1:
4 2
11 5 7 9
1 2
1 3
1 4
2 3
1 4
输出样例#1:
14
11
题解
线性基+链剖
用链剖维护每条链的线性基
然后对于线性基的合并就是把一个线性基每一位的数都当做一个新数插入另一个线性基中
注意链剖的时候要合并出来一个线性基表示这条路径的线性基,然后再贪心选取最大值
而不能直接简单的用一个变量去与路径上的每条链的线性基贪心的取\(max\)
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
# define ls (now << 1)
# define rs (now << 1 | 1)
const int M = 20005 ;
const int N = 63 ;
using namespace std ;
inline LL read() {
char c = getchar() ; LL x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
LL n , num , hea[M] , val[M] ;
LL fa[M] , size[M] , dep[M] , son[M] ;
LL cnt , ans , top[M] , id[M] , p[M] , w[N] ;
struct E { int nxt , to ; } edge[M * 2] ;
struct Node {
LL lib[N] ;
} t[M * 4] ;
inline void add_edge(int from , int to) {
edge[++num].nxt = hea[from] ;
edge[num].to = to ; hea[from] = num ;
}
void dfs1(int u , int father , int depth) {
fa[u] = father ; size[u] = 1 ; dep[u] = depth ; int mx = -1 ;
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ; if(v == father) continue ;
dfs1(v , u , depth + 1) ; size[u] += size[v] ; if(size[v] > mx) mx = size[v] , son[u] = v ;
}
}
void dfs2(int u , int topf) {
top[u] = topf ; id[u] = ++ cnt ; p[cnt] = val[u] ;
if(!son[u]) return ; dfs2(son[u] , topf) ;
for(int i = hea[u] ; i ; i = edge[i].nxt) {
int v = edge[i].to ; if(!id[v]) dfs2(v , v) ;
}
}
inline void Insert(LL *lib , LL x) {
for(int i = 61 ; i >= 0 ; i --)
if(x & (1LL << i)) {
if(!lib[i]) {
lib[i] = x ;
return ;
}
else x ^= lib[i] ;
}
}
inline void pushup(LL *lib , LL *A , LL *B) {
for(int i = 61 ; i >= 0 ; i --) lib[i] = A[i] ;
for(int i = 61 ; i >= 0 ; i --) if(B[i]) Insert(lib , B[i]) ;
}
void build(int l , int r , int now) {
if(l == r) {
Insert(t[now].lib , p[l]) ;
return ;
}
int mid = (l + r) >> 1 ;
build(l , mid , ls) ; build(mid + 1 , r , rs) ;
pushup(t[now].lib , t[ls].lib , t[rs].lib) ;
}
void query(int L , int R , int l , int r , int now) {
if(l > R || r < L) return ;
if(l >= L && r <= R) { pushup(w , w , t[now].lib) ; return ; }
int mid = (l + r) >> 1 ;
if(mid >= R) query(L , R , l , mid , ls) ;
else if(mid < L) query(L , R , mid + 1 , r , rs) ;
else query(L , mid , l , mid , ls) , query(mid + 1 , R , mid + 1 , r , rs) ;
}
inline LL getans(int x , int y) {
LL ans = 0 ;
memset(w , 0 , sizeof(w)) ;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x , y) ;
query(id[top[x]] , id[x] , 1 , n , 1) ;
x = fa[top[x]] ;
}
if(dep[x] > dep[y]) swap(x , y) ;
query(id[x] , id[y] , 1 , n , 1) ;
for(int i = 61 ; i >= 0 ; i --)
ans = max(ans , ans ^ w[i]) ;
return ans ;
}
int main() {
n = read() ; int Case = read() ;
for(int i = 1 ; i <= n ; i ++) val[i] = read() ;
for(int i = 1 , u , v ; i < n ; i ++) {
u = read() ; v = read() ;
add_edge(u , v) ; add_edge(v , u) ;
}
dfs1(1 , 1 , 1) ; dfs2(1 , 1) ;
build(1 , n , 1) ;
int x , y ;
while(Case --) {
x = read() ; y = read() ;
printf("%lld\n",getans(x , y)) ;
}
return 0 ;
}