#2013.「SCOI2016」幸运数字
内存限制:256 MiB时间限制:4000 ms标准输入输出
题目描述
A 国共有座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一。每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征。一些旅行者希望游览 A 国。旅行者计划乘飞机降落在号城市,沿着号城市到号城市之间那条唯一的路径游览,最终从城市起飞离开 A 国。
在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上。然而,幸运是不能简单叠加的,这一点游览者也十分清楚。他们迷信着幸运数字是以异或的方式保留在自己身上的。例如,游览者拍了张照片,幸运值分别是5、7、11,那么最终保留在自己身上的幸运值就是9(5^7^11)。
有些聪明的游览者发现,只要选择性地进行拍照,便能获得更大的幸运值。例如在上述三个幸运值中,只选择5 和11 ,可以保留的幸运值为14。现在,一些游览者找到了聪明的你,希望你帮他们计算出在他们的行程安排中可以保留的最大幸运值是多少。
输入格式
第一行包含两个正整数n、q,分别表示城市的数量和旅行者数量。
第二行包含个非负整数,其中第i个整数表示i号城市的幸运值。随后n-1行,每行包含两个正整数u,v,表示u号城市和v号城市之间有一条道路相连。
随后q行,每行包含两个正整数x、y,表示这名旅行者的旅行计划是从x号城市到y号城市。
输出格式
输出需要包含行,每行包含个非负整数,表示这名旅行者可以保留的最大幸运值。
样例
样例输入
4 2
11 5 7 9
1 2
1 3
1 4
2 3
1 4
样例输出
14
11
数据范围与提示
N≤2∗1e4
M≤2∗1e5
Ai≤2e60
倍增+线性基,在倍增的同时维护线性基,并且合并
F【i】【j】:第i个节点向上走2^j个节点的线性基
下附线性基板子:
struct L_B {
long long d[61], p[61];
int cnt;
L_B() {
memset(d, 0, sizeof(d));
memset(p, 0, sizeof(p));
cnt = 0;
}
bool Insert(long long val) {
for (int i = 60; i >= 0; i--) {
if (val & (1LL << i)) {
if (!d[i]) {
d[i] = val;
break;
}
val ^= d[i];
}
}
return val > 0;
}
long long query_max() {
long long ret = 0;
for (int i = 60; i >= 0; i--) {
if ((ret ^ d[i]) > ret)
ret ^= d[i];
}
return ret;
}
long long query_min() {
for (int i = 0; i <= 60; i++) {
if (d[i])
return d[i];
}
return 0;
}
void rebuild() {
for (int i = 60; i >= 0; i--) {
for (int j = i - 1; j >= 0; j--) {
if (d[i] & (1LL << j)) {
d[i] ^= d[j];
}
}
}
for (int i = 0; i <= 60; i++) {
if (d[i]) {
p[cnt++] = d[i];
}
}
}
long long kthquery(long long k) {
long long ret = 0;
if (k >= (1LL << cnt)) {
return -1;
}
for (int i = 60; i >= 0; i--) {
if (k & (1LL << i)) {
ret ^= p[i];
}
}
return ret;
}
};
//将一个线性基暴力插入另一个线性基即可
void Merge(L_B &F, L_B &G) {
for (int i = 60; i>=0; i --)
if (G.d[i]) F.Insert(G.d[i]);
}
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
ll a[maxn];
int n;
vector <int> G[maxn];
int fa[20005][16],dep[20005];
struct L_B{
long long d[61];
L_B()
{
memset(d, 0, sizeof(d));
}
bool Insert(long long val)
{
for(int i = 60; i >= 0; i--)
{
if(val & (1LL << i))
{
if(!d[i])
{
d[i] = val;
break;
}
val ^= d[i];
}
}
return val > 0;
}
long long query_max()
{
long long ret = 0;
for(int i = 60;i >= 0; i--)
{
if((ret ^ d[i]) > ret)
ret ^= d[i];
}
return ret;
}
}F[20005][16],Ans;
//将一个线性基暴力插入另一个线性基即可
void Merge(L_B &F, L_B &G) {
for (int i = 60; i>=0; i --)
if (G.d[i]) F.Insert(G.d[i]);
}
void dfs(int u,int pre)
{
dep[u]=dep[pre]+1;
fa[u][0]=pre;
for(auto x:G[u])
{
if(x==pre) continue;
dfs(x,u);
}
}
void Prepare()
{
for (int j=1; j<=15; j++)
{
for(int i=1; i<=n; i++)
{
fa[i][j]=fa[fa[i][j - 1]][j - 1];
for(int k=60;k>=0;k--)
{
F[i][j].d[k]=F[i][j-1].d[k];
}
Merge(F[i][j],F[fa[i][j-1]][j-1]);
}
}
}
void lca(int a,int b)
{
if(dep[a]<dep[b]) swap(a,b);
int d=dep[a]-dep[b];
for(int i=0;(1<<i)<=d;i++)
{
if((1<<i)&d)
{
Merge(Ans,F[a][i]);
a=fa[a][i];
}
}
if(a==b)
{
Merge(Ans,F[a][0]);
return;
}
for(int i=15;i>=0;i--)
{
if(fa[a][i]!=fa[b][i])
{
Merge(Ans, F[a][i]);
Merge(Ans, F[b][i]);
a=fa[a][i];
b=fa[b][i];
}
}
//~~!!!!特别重要啊!!!!
Merge(Ans,F[a][0]);
Merge(Ans,F[b][0]);
Merge(Ans,F[fa[a][0]][0]);
}
int main()
{
int Q,u,v;
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
F[i][0].Insert(a[i]);
}
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0);//得到第i个节点的深度和父亲
Prepare();//维护F[i][j]
while(Q--)
{
for(int i=60;i>=0;i--)
Ans.d[i]=0; //清空Ans线性基
scanf("%d%d",&u,&v);
lca(u,v);
printf("%lld\n",Ans.query_max());
}
return 0;
}