BZOJ - 4568 幸运数字

题意:

  给出一颗带点权的树。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\n", ans);
    }
}
View Code

 

posted @ 2018-05-25 19:20  Pneuis  阅读(250)  评论(0编辑  收藏  举报