bzoj4568
scoi2016 幸运数字
1 题目描述
-
A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一。每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征。
一些旅行者希望游览 A 国。旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y 城市起飞离开 A 国。在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上。然而,幸运是不能简单叠加的,这一点游览者也十分清楚。他们迷信着幸运数字是以异或的方式保留在自己身上的。
例如,游览者拍了 3 张照片,幸运值分别是 5,7,11,那么最终保留在自己身上的幸运值就是 9\((5\oplus 7 \oplus 11)\)。
有些聪明的游览者发现,只要选择性地进行拍照,便能获得更大的幸运值。例如在上述三个幸运值中,只选择 5 和 11 ,可以保留的幸运值为 14 。现在,一些游览者找到了聪明的你,希望你帮他们计算出在他们的行程安排中可以保留的最大幸运值是多少。
2 分析
- 本题可以看成是树上的线性基问题,我们要求两点之间的线性基,我们只要维护\(f[i][j]\)表示i到距离为\(2^j\)的编号,此外\(d[i][j][k]\),这是i到\(f[i][j]\)的第k个线性基。
- 本题的关键是如何维护d数组,其实我们只要在倍增的时候暴力维护就可以了。
- 求x到y之间的线性基,我们只要倍增暴力维护x到z的线性基,y到z的线性基,然后暴力合并线性基。
- 时间复杂度:\(O(n \times \log_{2}{n}\times 60)\)。 这样勉强也不会爆。
3 代码
#include<bits/stdc++.h>
using namespace std;
int const N=20000+10;
#define ll long long
struct edge{
int to,nt;
}e[N<<1];
int f[N][15],tin[N],tout[N],sum,n,q,cnt,h[N];
ll d[N][15][61],val[N],t[61];
void add(int a,int b){
e[++cnt].to=b; e[cnt].nt=h[a]; h[a]=cnt;
}
void ins(ll *d,ll x){
for(int i=60;i>=0;i--)
if(x&(1LL<<i)){
if(d[i]) x^=d[i];
else {
d[i]=x;
break;
}
}
}
void dfs(int x,int fa){
tin[x]=++sum;
f[x][0]=fa;
ins(d[x][0],val[x]);
ins(d[x][0],val[fa]);
for(int i=h[x];i;i=e[i].nt){
int v=e[i].to;
if(v==fa) continue;
dfs(v,x);
}
tout[x]=++sum;
}
void merge(ll *d,ll *d1,ll *d2){
for(int i=0;i<=60;i++)
if(d1[i]) ins(d,d1[i]);
for(int i=0;i<=60;i++)
if(d2[i]) ins(d,d2[i]);
}
int ancestor(int x,int y){
return tin[x]<=tin[y] && tout[y]<=tout[x];
}
int lca(int x,int y){
if(ancestor(x,y)) return x;
if(ancestor(y,x)) return y;
for(int i=14;i>=0;i--)
if(!ancestor(f[x][i],y))
x=f[x][i];
return f[x][0];
}
void merge2(ll *d,ll *d1){
for(int i=0;i<=60;i++)
if(d1[i]) ins(d,d1[i]);
}
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%lld",&val[i]);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
tin[0]=1;tout[0]=2*n;
dfs(1,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=14;j++){
f[i][j]=f[f[i][j-1]][j-1];
merge(d[i][j],d[i][j-1],d[f[i][j-1]][j-1]);
}
while (q--){
int x,y;
scanf("%d%d",&x,&y);
int z=lca(x,y);
memset(t,0,sizeof(t));
int k=x;
for(int i=14;i>=0;i--)
if(!ancestor(f[k][i],z))
merge2(t,d[k][i]),k=f[k][i];
if(x!=z) merge2(t,d[k][0]);
k=y;
for(int i=14;i>=0;i--)
if(!ancestor(f[k][i],z))
merge2(t,d[k][i]),k=f[k][i];
if(y!=z) merge2(t,d[k][0]);
if(z==x && z==y) ins(t,val[z]);
ll ans=0;
for(int i=60;i>=0;i--)
ans=max(ans,ans^t[i]);
printf("%lld\n",ans);
}
return 0;
}