幸运数字
题目描述
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 个非负整数,表示这名旅行者可以保留的最大幸运值。
输入输出样例
14
11
分析:
树上线性基的模板。
显然我们的做法应该是把树上路径的每个点构造成线性基然后求异或最大值。但问题在于我们如何快速构造出任意一条路径的线性基。
这里采用倍增的思想。
我们开一个三维数组$p[i][j][k]$表示从$i$跳$2^j$时路径上的线性基。在预处理倍增的时候,我们只要把两段$2^(j-1)$的线性基合并就能得到$2^j$的线性基。查询$lca$的时候用同样的方法就能得到一条路径上的线性基。
代码略长,不太好看。
Code:
//It is made by HolseLee on 13th Sep 2018 //Luogu.org P3292 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define Max(a,b) (a)>(b)?(a):(b) using namespace std; typedef long long ll; const int N=2e4+7; int n,m,head[N],cnte,f[N][22],dep[N]; ll p[N][22][62],ans[65]; struct Edge{ int to,nxt; Edge() {} Edge(int _x,int _y): to(_x),nxt(_y) {} }e[N<<1]; inline ll read() { char ch=getchar(); ll num=0; bool flag=false; while( ch<'0' || ch>'9' ) { if( ch=='-' ) flag=true; ch=getchar(); } while( ch>='0' && ch<='9' ) { num=num*10+ch-'0'; ch=getchar(); } return flag ? -num : num ; } inline void add(int x,int y) { e[++cnte]=Edge(y,head[x]); head[x]=cnte; } void dfs(int u,int las) { f[u][0]=las; dep[u]=dep[las]+1; for(int i=head[u]; i; i=e[i].nxt) { if( e[i].to==las ) continue; dfs(e[i].to,u); } } inline void liner(ll *x,ll y) { for(int i=61; i>=0; --i) { if( (y>>i)&1 ) { if( !x[i] ) { x[i]=y; break; } else { y^=x[i]; } } } } inline void merge(ll *x,ll *y) { for(int i=61; i>=0; --i) { if( y[i] ) liner(x,y[i]); } } void ready() { for(int j=1; j<=21; ++j) for(int i=1; i<=n; ++i) { f[i][j]=f[f[i][j-1]][j-1]; memcpy(p[i][j],p[i][j-1],sizeof(p[i][j-1])); merge(p[i][j],p[f[i][j-1]][j-1]); } } inline void lca(int x,int y) { if( dep[x]<dep[y] ) swap(x,y); for(int i=21; i>=0; --i) if( dep[f[x][i]]>=dep[y] ) merge(ans,p[x][i]), x=f[x][i]; if( x==y ) { merge(ans,p[x][0]); return; } for(int i=21; i>=0; --i) if( f[x][i]!=f[y][i] ) { merge(ans,p[x][i]), merge(ans,p[y][i]); x=f[x][i], y=f[y][i]; } merge(ans,p[x][0]), merge(ans,p[y][0]); merge(ans,p[f[x][0]][0]); } int main() { n=read(); m=read(); for(int i=1; i<=n; ++i) { liner(p[i][0],read()); } int x,y; for(int i=1; i<n; ++i) { x=read(), y=read(); add(x,y); add(y,x); } dfs(1,0); ready(); ll ret; for(int i=1; i<=m; ++i) { x=read(), y=read(); ret=0; memset(ans,0,sizeof(ans)); lca(x,y); for(int i=61; i>=0; --i) { ret=Max(ret,ret^ans[i]); } printf("%lld ",ret); } return 0; }