zoukankan      html  css  js  c++  java
  • 【暖*墟】#数据结构# 可持久化Trie 与 XOR问题

    0/1 Trie

    【例题】最长异或路径

    • 给定一棵n个点的带权树,求树中最长的异或路径。

    Solution 01字典树:用于解决xor问题。

    • 用dis[i]表示‘从i点到根节点的路径异或和’。
    • ---> 那么问题转化为:求两点dis的异或最大值。

    一般查询两数的最大异或值时,都是从最高位到最低位,由此建立Trie树。

    利用贪心的思想:对 dis[ ] 建一棵trie树,对于每个数,每次选相反的位置。

    • 即:如果x这一位是1,在tire树上往0跑,反之往1跑。
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<string>
    #include<queue>
    #include<vector>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    //【p4551】最长异或路径
    // 给定一棵n个点的带权树,求树中最长的异或路径。
    
    /*【01字典树】用于解决xor问题
    用dis[i]表示‘从i点到根节点的路径异或和’。
    ---> 那么问题转化为:求两点dis的异或最大值。
    一般查询两数的最大异或值时,从最高位到最低位;
    贪心:对dis建一棵trie数,对于每个数,每次选相反的位置。 
    即:如果x这一位是1,在tire树上往0跑,反之往1跑。*/
    
    void reads(int &x){ //读入优化(正负整数)
        int fx=1;x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
        x*=fx; //正负号
    }
    
    //------------建边+dfs求异或前缀和-------------//
    
    const int N=1000019; int dis[N],head[N],cnt,tot=1;
    
    struct node{ int ver,nextt,w; }e[N<<1];
    
    void add(int x,int y,int w)
     { e[++cnt].ver=y,e[cnt].nextt=head[x],e[cnt].w=w,head[x]=cnt; }
    
    void dfs(int x,int fa){
        for(int i=head[x];i;i=e[i].nextt){
            int v=e[i].ver; if(v==fa) continue;
            dis[v]=dis[x]^e[i].w; dfs(v,x);
        }
    }
    
    //------------0/1 Trie-------------//
    
    struct Trie_{ int ch[2]; }trie[N<<2]; //01字典树
    
    void build(int x){
        int p=1;
        for(int i=31;i>=0;i--){ //注意:从高位到低位加入trie树 
        //↑↑如果把i写成32就会出问题...(样例数据答案从7-->8...)
            int c=(x>>i)&1; //找到对应字符
            if(!trie[p].ch[c]) trie[p].ch[c]=++tot;
            p=trie[p].ch[c]; //继续向下走
        }
    }
    
    int find_(int x){
        int p=1,ans=0;
        for(int i=31;i>=0;i--){
            int c=((x>>i)&1)^1; //找相反字符
            if(trie[p].ch[c]) p=trie[p].ch[c],ans+=(1<<i);
            else p=trie[p].ch[c^1]; //继续向下走
        } return ans; //和x的最大异或值
    }
    
    //------------主程序-------------//
    
    int main(){
        int n,ans=0; reads(n);
        for(int i=1,x,y,z;i<n;i++) 
            reads(x),reads(y),reads(z),add(x,y,z),add(y,x,z);
        dfs(1,0); //↓↓将dis数组(每个点的异或前缀和)放入trie树
        for(int i=1;i<=n;i++) build(dis[i]);
        for(int i=1;i<=n;i++) ans=max(ans,find_(dis[i]));
        printf("%d
    ",ans); //↑↑对每个点找全然相反的那个
    }

    可持久化Trie

    在一棵树维护每个前缀出现的次数(用类似 trie 的做法)。

    记录 在Trie树上有相同前缀 的前缀和(节点的个数),

    通过取差值(右边界减去左边界)判断一段区间内是否有字典树上的前缀。

    做法就是对于每一个节点新建一颗字典树,记录下节点对应的字典树根的位置。

    在插入过程中,对于插入的数的所有前缀都新建一个节点,

    他拥有 字典树上相同前缀 的前缀和就是他父亲(此题是序列中的前一个数)的前缀和加一,

    其他的前缀全部指向父亲的对应节点,因为前缀和没有改变。

    【例题】P4098 ALO

    • 现在你拥有n颗宝石,每颗宝石有一个能量密度ai,能量密度两两不同。
    • 选取连续的一些宝石(必须多于一个)进行融合,设为 ai, ai+1, …, aj,
    • 融合而成的宝石的能量密度 = 能量密度次大值 ^ 其他任意一颗宝石的能量密度。
    • 现在你需要知道你怎么选取需要融合的宝石,才能使生成的宝石能量密度最大。

    【可持久化Trie树 + STL-set】

    不妨设当前数字左边第一个比它大的下标为l1,第二个比它大的记作l2。

    同理设当前数字右边第一个比它大的下标为r1​,第二个比它大的记作r2​。

    那么对于一个数字来说,它能作为次大值的区间有很多,但我们只取两个区间:

    • [l1+1,R2−1]和[l2+1,R1−1]。因为其他的区间都是这两个区间的子集。

    可持久化Trie树按位从大到小贪心。需要注意set的边界问题:加入0和n+1,防止越界。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<string>
    #include<queue>
    #include<vector>
    #include<cmath>
    #include<map>
    #include<set>
    using namespace std;
    typedef long long ll;
    
    /*【p4098】ALO
    现在你拥有n颗宝石,每颗宝石有一个能量密度ai,能量密度两两不同。
    选取连续的一些宝石(必须多于一个)进行融合,设为 ai, ai+1, …, aj,
    融合而成的宝石的能量密度 = 能量密度次大值 ^ 其他任意一颗宝石的能量密度。
    现在你需要知道你怎么选取需要融合的宝石,才能使生成的宝石能量密度最大。*/ 
    
    /*【可持久化Trie树 + STL-set】
    处理出每个数可能是哪些区间的次大值。考虑将所有数从大到小排序,一个一个扔进set中。
    某个数成为次小值的区间最大范围:最左端为 (前驱的前驱,后继) ,最右端为 (前驱,后继的后继) 。
    那么它们的并集就是 (前驱的前驱,后继的后继) ,即所有数都可能出现在以当前数为次大值的区间中。
    可持久化Trie树按位从大到小贪心。需要注意set的边界问题,建议把0和n+1加入到set中防止越界。*/
    
    void reads(int &x){ //读入优化(正负整数)
        int fx=1;x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
        x*=fx; //正负号
    }
    
    const int N=100019; 
    
    struct data{ int v,id; }a[N];
    
    set<int> s; set<int>::iterator it;
    
    int ch[N*30][2],siz[N*30],tot,rt[N];
    
    bool cmp(data a,data b){ return a.v>b.v; }
    
    //------------可持久化Trie-------------//
    
    int insert(int x,int v){ int now,y; bool t; now=y=++tot; 
      for(int i=1<<30;i;i>>=1) t=v&i,ch[y][t^1]=ch[x][t^1],
        x=ch[x][t],ch[y][t]=++tot,y=ch[y][t],siz[y]=siz[x]+1; return now; }
    
    int query(int x,int y,int v){
        int anss=0; bool t;
        for(int i=1<<30;i;i>>=1){ t=v&i;
            if(siz[ch[y][t^1]]-siz[ch[x][t^1]])
                anss|=i,x=ch[x][t^1],y=ch[y][t^1];
            else x=ch[x][t],y=ch[y][t];
        } return anss; //在可持久化trie树上贪心
    }
    
    //------------主程序-------------//
    
    int main(){
        int n,ans=0,l,r; reads(n);
        for(int i=1;i<=n;i++) reads(a[i].v),
            a[i].id=i,rt[i]=insert(rt[i-1],a[i].v);
        sort(a+1,a+n+1,cmp),s.insert(0),s.insert(n+1);
        for(int i=1;i<=n;i++){ //↑↑防止set越界
            it=s.upper_bound(a[i].id),it++;
            if(it==s.end()) r=n; else r=*it-1; it--,it--;
            if(it==s.begin()) l=1; else l=*(--it)+1; s.insert(a[i].id); 
            if(i!=1) ans=max(ans,query(rt[r],rt[l-1],a[i].v));
        } printf("%d
    ",ans);
    }

                                           ——时间划过风的轨迹,那个少年,还在等你

  • 相关阅读:
    高效 告别996,开启java高效编程之门 问题
    高效 告别996,开启java高效编程之门 3-3实战:利用Lambda+Stream处理业务逻辑
    高效 告别996,开启java高效编程之门 3-2传统方式处理业务逻辑
    高效 告别996,开启java高效编程之门 3-1流式编程开场与案例场景概述
    无限树
    拖拽示例
    日期格式化字符串 字符串转化成日期
    loading
    It运维项目整理
    免费的二维码发布平台 http://zhifubao.masao.top:8282/assets/index.html
  • 原文地址:https://www.cnblogs.com/FloraLOVERyuuji/p/10456880.html
Copyright © 2011-2022 走看看