题意:
给出一颗带点权的树。q次询问,每次询问给出点u,v。在两点路径上选出一些点,使其点权异或和最大。
题解:
倍增的合并树上的线性基。对于每次询问,将路径上的点倍增的合并。最后贪心的从高位开始取最大值。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e4+10; int n, q, tot; int u, v; int head[N], to[N<<1], nxt[N<<1]; int depth[N], fa[N][20]; ll val, ans; ll dp[N][20][65], tt[65]; void add(ll *a, ll b) { for(int i = 62; i >= 0; i--) if((1ll<<i)&b) { if(!a[i]) { a[i] = b; return ; } else b ^= a[i]; } } void dfs(int x, int pre, int d) { fa[x][0] = pre; depth[x] = d; for(int i = head[x]; ~i; i = nxt[i]) if(to[i] != pre) dfs(to[i], x, d+1); } int main() { scanf("%d%d", &n, &q); memset(head, -1, sizeof(head)); for(int i = 1; i <= n; i++) scanf("%lld", &val), add(dp[i][0], val); for(int i = 1; i < n; i++) { scanf("%d%d", &u, &v); to[++tot] = u; nxt[tot] = head[v]; head[v] = tot; to[++tot] = v; nxt[tot] = head[u]; head[u] = tot; } dfs(1, 0, 0); for(int i = 0; i < 16; i++) { for(int j = 1; j <= n; j++) { v = fa[j][i]; fa[j][i+1] = fa[v][i]; for(int k = 62; k >= 0; k--) { add(dp[j][i+1], dp[j][i][k]); add(dp[j][i+1], dp[v][i][k]); } } } while(q--) { ans = 0; memset(tt, 0, sizeof(tt)); scanf("%d%d", &u, &v); if(depth[u]<depth[v]) swap(u, v); int k = depth[u]-depth[v]; for(int i = 16; i >= 0; i--) { if(k&(1<<i)) { for(int j = 62; j >= 0; j--) if(dp[u][i][j]) add(tt, dp[u][i][j]); u = fa[u][i]; } } for(int i = 16; i >= 0; i--) { if(fa[u][i] != fa[v][i]) { for(int j = 62; j >= 0; j--) if(dp[u][i][j]) add(tt, dp[u][i][j]); for(int j = 62; j >= 0; j--) if(dp[v][i][j]) add(tt, dp[v][i][j]); u = fa[u][i]; v = fa[v][i]; } } for(int j = 62; j >= 0; j--) if(dp[u][0][j]) add(tt, dp[u][0][j]); for(int j = 62; j >= 0; j--) if(dp[v][0][j]) add(tt, dp[v][0][j]); if(u != v) for(int j = 62; j >= 0; j--) if(dp[fa[u][0]][0][j]) add(tt, dp[fa[u][0]][0][j]); for(int i = 62; i >= 0; i--) if((ans^tt[i]) > ans) ans = ans^tt[i]; printf("%lld ", ans); } }