hdu4757 Tree(可持久化字典树+lca)

题意:

给一棵n个节点的树,每个点有点权
q组询问,每组询问给x,y,v,问x到y路径上的点异或v的最大值是多少

数据范围:n,q<=1e5

解法:

树上路径用lca搞,最大异或和用trie搞,在树上建可持久化trie就行了

code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int maxd=20;
struct Node{
    int a[N<<6][2];//节点和边
    int cnt[N<<6];//路径数
    int r[N],tot;//root
    void init(){
        for(int i=0;i<=tot;i++){
            a[i][0]=a[i][1]=0;
            cnt[i]=0;
        }
        tot=0;
    }
    int add(int x,int last){//插入x
        int k=++tot;//开一个新节点
        int node=k;
        for(int i=maxd;i>=0;i--){
            a[node][0]=a[last][0];//复制
            a[node][1]=a[last][1];
            int v=(x>>i&1);
            a[node][v]=++tot;
            node=a[node][v];
            last=a[last][v];
            cnt[node]=cnt[last]+1;//路径数复制后+1
        }
        return k;
    }
    int ask(int val,int x,int y,int lc,int flc){
        int c[2];
        int ans=0;
        for(int i=maxd;i>=0;i--){
            c[0]=cnt[a[x][0]]-cnt[a[lc][0]]+cnt[a[y][0]]-cnt[a[flc][0]];
            c[1]=cnt[a[x][1]]-cnt[a[lc][1]]+cnt[a[y][1]]-cnt[a[flc][1]];
            int v=(val>>i&1);
            if(c[v^1]){
                ans|=(1<<i);
                x=a[x][v^1];
                y=a[y][v^1];
                lc=a[lc][v^1];
                flc=a[flc][v^1];
            }else{
                x=a[x][v];
                y=a[y][v];
                lc=a[lc][v];
                flc=a[flc][v];
            }
        }
        return ans;
    }
}T;
vector<int>g[N];
int f[N][25],d[N];
int a[N];
int n,q;
void dfs(int x,int fa,int dep){
    d[x]=dep;
    for(int j=1;j<=maxd;j++){
        f[x][j]=f[f[x][j-1]][j-1];
    }
    T.r[x]=T.add(a[x],T.r[fa]);
    for(int v:g[x]){
        if(v==fa)continue;
        d[v]=d[x]+1;
        f[v][0]=x;
        dfs(v,x,dep+1);
    }
}
int lca(int a,int b){
    if(d[a]<d[b])swap(a,b);//把a定为更深的
    for(int i=maxd;i>=0;i--){//爬到同一高度
        if(d[f[a][i]]>=d[b]){
            a=f[a][i];
        }
    }
    if(a==b)return a;
    for(int i=maxd;i>=0;i--){
        if(f[a][i]!=f[b][i]){
            a=f[a][i];
            b=f[b][i];
        }
    }
    return f[a][0];
}
int solve(int x,int y,int v){
    int lc=lca(x,y);
    int ans=T.ask(v,T.r[x],T.r[y],T.r[lc],T.r[f[lc][0]]);
    return ans;
}
signed main(){
    while(scanf("%d%d",&n,&q)!=EOF){
        T.init();
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=n;i++)g[i].clear();
        for(int i=1;i<n;i++){
            int a,b;scanf("%d%d",&a,&b);
            g[a].push_back(b);
            g[b].push_back(a);
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=maxd;j++){
                f[i][j]=0;
            }
        }
        dfs(1,1,0);
        while(q--){
            int x,y,v;scanf("%d%d%d",&x,&y,&v);
            int ans=solve(x,y,v);
            printf("%d\n",ans);
        }
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值