#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;
}