可持久化Trie

可持久化Trie--区间异或最值问题

应用:在 \(O(logn)\) 时间复杂度解决 查询区间 \([l,r]\) 内与数 \(x\) 异或的最大值。

插入操作:把 \(n\) 个整数插入 \(01Trie\),生成 \(n\) 个版本的可持久化树。

查询操作:利用前缀和与差分的思想,用两颗前缀 \(Trie\) 树相减得到该区间的 \(Trie\) 树。查询的时候,利用贪心思想,尽量与当前位不同的地方跳即可。

模板

//可持久化Trie
struct Trie{
   int ch[N<<5][2];
   int rt[N];
   int sz[N<<5];
   int idx=0,cnt=0;
   void insert(int v){
      rt[++idx]=++cnt;//新根开点
      int x=rt[idx-1];//旧版
      int y=rt[idx];
      for(int i=30;i>=0;i--){
         int j=v>>i&1;
         ch[y][j^1]=ch[x][j^1];//异位继承
         ch[y][j]=++cnt;//新位
         x=ch[x][j];y=ch[y][j];
         sz[y]=sz[x]+1;
      }
   }
   int query(int x,int y,int v){
      int ret=0;
      for(int i=30;i>=0;i--){
         int j=(v>>i&1);
         if(sz[ch[y][j^1]]>sz[ch[x][j^1]])
            x=ch[x][j^1],y=ch[y][j^1],ret|=(1LL<<i);
         else
            x=ch[x][j],y=ch[y][j];
      }
      return ret;
   }
}trie;

查询 \([l,r]\)\(x\) 的最大异或值时\(query(trie.rt[l-1],trie.rt[r])\)

P4592 [TJOI2018] 异或 树链剖分+可持久化Trie

题目链接

一道模板题,解决区间异或最大值--可持久化Trie。然后用树链剖分把树上问题转化成链上问题即可。

注意插入的时候要按照 \(dfs\) 序插入Trie中。

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

const int N =1e5+10;

int a[N];

//可持久化Trie
struct Trie{
   int ch[N<<5][2];
   int rt[N];
   int sz[N<<5];
   int idx=0,cnt=0;
   void insert(int v){
      rt[++idx]=++cnt;//新根开点
      int x=rt[idx-1];//旧版
      int y=rt[idx];
      for(int i=30;i>=0;i--){
         int j=v>>i&1;
         ch[y][j^1]=ch[x][j^1];//异位继承
         ch[y][j]=++cnt;//新位
         x=ch[x][j];y=ch[y][j];
         sz[y]=sz[x]+1;
      }
   }
   int query(int x,int y,int v){
      int ret=0;
      for(int i=30;i>=0;i--){
         int j=(v>>i&1);
         if(sz[ch[y][j^1]]>sz[ch[x][j^1]])
            x=ch[x][j^1],y=ch[y][j^1],ret|=(1LL<<i);
         else
            x=ch[x][j],y=ch[y][j];
      }
      return ret;
   }
}trie;

//树链剖分
vector<int> e[N];
int fa[N],dep[N],son[N],sz[N],id[N],w[N];
int top[N],dfn;
//预处理各个信息
void dfs1(int u,int father){
    fa[u]=father;
    dep[u]=dep[father]+1;
    sz[u]=1;
    for(auto v:e[u]){
        if(v==father) continue;
        dfs1(v,u);
        sz[u]+=sz[v];
        if(sz[son[u]]<sz[v]) son[u]=v;
    }
}

void dfs2(int u,int tp){
    id[u]=++dfn;
    w[dfn]=a[u];
    top[u]=tp;
    if(!son[u]) return;
    dfs2(son[u],tp);
    for(auto v:e[u]){
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

int qRange(int u,int v,int val){
    int res=0,l,r;
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        //线段树区间查询[id[top[u]],id[u]]
        l=trie.rt[id[top[u]]-1];
        r=trie.rt[id[u]];
        res=max(res,trie.query(l,r,val));
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    //加上两点之间的答案[id[u],id[v]]
    l=trie.rt[id[u]-1];
    r=trie.rt[id[v]];
    res=max(res,trie.query(l,r,val));
    return res;
}

int qSon(int u,int v){
    //线段树查询[id[u],id[u]+sz[u]-1]
    int l=trie.rt[id[u]-1],r=trie.rt[id[u]+sz[u]-1];
    return trie.query(l,r,v);
}

void Showball(){
    int n,q;
    cin>>n>>q;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs1(1,-1);
    dfs2(1,1);
    vector<int> p(n+1);
    iota(p.begin(),p.end(),0);
    sort(p.begin()+1,p.end(),[&](int x,int y){
        return id[x]<id[y];
    });
    for(int i=1;i<=n;i++){
        trie.insert(a[p[i]]);
    }

    while(q--){
        int op;
        cin>>op;
        if(op==1){
            int x,z;
            cin>>x>>z;
            cout<<qSon(x,z)<<"\n";
        }else{
            int x,y,z;
            cin>>x>>y>>z;
            cout<<qRange(x,y,z)<<"\n";
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}
posted @ 2024-12-09 21:28  Showball  阅读(4)  评论(0编辑  收藏  举报