hdu4757 Tree

可持久化Trie,比赛的时候一直在想不到怎么样才可以做减法,赛后才想清楚。

首先异或最大就是在字典树上找它的按位取反,首先满足高位。

对每个节点维护一个Trie,维护的是它到根所有路过的节点的权值,维护Trie的时候同时维护一下子树的叶子节点个数。

对于每一个询问先倍增求出LCA,再由三个节点的权值来计算某子树的叶子节点个数,判断能不能走到某个儿子,如果某棵子树没有叶子节点就意味着不能走。

说的比较抽象,看代码比较好懂吧。。大概

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <list>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
//#pragma comment(linker, "/STACK:16777216")
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
#define N 100010
#define M 2000000
int u[M][2] , v[M] , cnt;
int n , m , a[N] , L[N] , f[17][N] , pre[N] , mcnt;
struct edge
{
    int x , next;
}e[N << 1];
int str[20];
int root[N];
int newnode()
{
    memset(u[cnt] , 0 , sizeof(u[cnt]));
    v[cnt] = 0;
    return cnt ++;
}

void insert(int x , int y , int val)
{
    x = root[x] , y = root[y];
    for (int i = 0 ; i < 16 ; ++ i)
    {
        int c = val >> (16 - i - 1) & 1;
        if (!u[x][c])
          u[x][c] = newnode() , u[x][!c] = u[y][!c] , v[u[x][c]] = v[u[y][c]];
        x = u[x][c] , y = u[y][c];
        ++ v[x];
    }
}
int query(int x , int y , int z , int val)
{
    int ans = 0 , res = a[z] ^ val;
    x = root[x] , y = root[y] , z = root[z];
    for (int i = 0 ; i < 16 ; ++ i)
    {
        int c = !(val >> (16 - i - 1) & 1);
        if (v[u[x][c]] + v[u[y][c]] - v[u[z][c]] - v[u[z][c]] > 0)
            ans |= 1 << (16 - i - 1);
        else
            c = !c;
        x = u[x][c] , y = u[y][c] , z = u[z][c];
    }
    return max(ans , res);
}


void dfs(int x , int fa)
{
    f[0][x] = fa , L[x] = L[fa] + 1 ;
    root[x] = newnode();
    insert(x , fa , a[x]);
    for (int i = pre[x] ; ~i ; i = e[i].next)
        if (e[i].x != fa)
            dfs(e[i].x , x);
}

int LCA(int x , int y)
{
    if (L[x] > L[y]) swap(x , y);
    for (int i = 16 ; i >= 0 ; -- i)
        if (L[y] - L[x] >= 1 << i)
            y = f[i][y];
    if (x == y) return y;
    for (int i = 16 ; i >= 0 ; -- i)
        if (f[i][x] && f[i][x] != f[i][y])
            x = f[i][x] , y = f[i][y];
    return f[0][x];
}

void work()
{
    int  i , j , x , y , z , w;
    memset(pre , -1 , sizeof(pre));
    mcnt = 0 , cnt = 1;
    for (i = 1 ; i <= n ; ++ i)
        scanf("%d",&a[i]);
    for (i = 1 ; i < n ; ++ i)
    {
        scanf("%d%d",&x,&y);
        e[mcnt].x = y , e[mcnt].next = pre[x] , pre[x] = mcnt ++;
        e[mcnt].x = x , e[mcnt].next = pre[y] , pre[y] = mcnt ++;
    }
    memset(f , 0 , sizeof(f));
    dfs(1 , 0);
    for (j = 1 ; 1 << j < n ; ++ j)
        for (i = 1 ; i <= n ; ++ i)
            f[j][i] = f[j - 1][f[j - 1][i]];
    while (m --)
    {
        scanf("%d%d%d",&x,&y,&w);
        z = LCA(x , y);
        printf("%d\n" , query(x , y , z , w));
    }
}

int main()
{
    freopen("~input.txt" , "r" , stdin);
    while (~scanf("%d%d",&n,&m))
        work();
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值