• cf842D 01字典树|线段树 模板见hdu4825


    一般异或问题都可以转换成字典树的问题,,我一开始的想法有点小问题,改一下就好了

    下面的代码是逆向建树的,数据量大就不行

    /*3
    01字典树
    根据异或性质,a1!=a2 ==> a1^x1^..^xn != a2^x1^..an 
    把修改转换成不同的询问
    先把初始集合里没有的数建立成字典树
    每次询问找的是字典树里异或x最小的值 
    */
    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 300005
    int buf[maxn];
    void getbuf(int a){
        for(int i=1;i<=20;i++)
            buf[i]=a%2,a/=2;
        for(int i=1,j=20; i<=j; i++,j--)         
            swap(buf[i],buf[j]);
    }
    
    struct Trie{
        int root,L;
        int nxt[maxn*30][2],end[maxn*30];
        int newnode(){
            nxt[L][0]=nxt[L][1]=-1;
            return L++;
        }
        void init(){
            L=0;root=newnode();
        }
        void insert(int a){
            getbuf(a);
    //for(int i=1;i<=20;i++)printf("%d ",buf[i]);
            int now=root;
            for(int i=1;i<=20;i++){
                if(nxt[now][buf[i]]==-1)
                    nxt[now][buf[i]]=newnode();
                now=nxt[now][buf[i]]; 
            }
            end[now]=a;
        }
        int query(int a){//要找和a异或最小的数,就是碰到1时就往1走,碰到0时就往0走 
            getbuf(a);
    //for(int i=1;i<=20;i++)printf("%d ",buf[i]);
            int now=root;
            for(int i=1;i<=20;i++){
                if(nxt[now][buf[i]]==-1)
                    now=nxt[now][buf[i]^1];
                else now=nxt[now][buf[i]];
            }
            return end[now];
        }
    }tr;
    int n,m,flag[maxn],a,Max,x;
    vector<int>v;
    int main(){
        tr.init();
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            cin>>a;flag[a]=1,Max=max(a,Max);
        }
        for(int i=0;i<=300000;i++)
            if(flag[i]==0)v.push_back(i);
        for(int i=0;i<v.size();i++)
            tr.insert(v[i]);
    
        m--,cin>>x;
        printf("%d
    ",tr.query(x)^x);
        while(m--){
            cin>>a;
            x^=a;
            printf("%d
    ",tr.query(x)^x);
        }
    }

    如果是把集合中存在的元素进行建树,就不会出现字典树大小无法确定的问题,但是每次查询要改一下,即如果第i位是1,那就往字典树的0子树找,反之往1子树找,并且如果先找的子树已经满了,即mex的结果不可能再这棵子树中找到,那么就往另一颗子树找即可

    #include <stdio.h>
    #include <string.h>
    #include<iostream>
    #define MAX(a,b) ((a)>(b)?(a):(b))
    #define NODE 3200010
    #define N 300010
    using namespace std;
    int n;
    int v[N];
    int node;
    int next[NODE][2];
    int end[NODE];
    int num[NODE][2];
    bool vis[NODE];
    void add(int cur,int k)
    {
        memset(next[node],0,sizeof(next[node]));
        end[node]=0;
        next[cur][k]=node++;
    }
    int cal(int x)
    {
        int i,k,cur=0,t1;
        int res=0;
        for(i=19;i>=0;i--)
        {
            k=((1<<i)&x)?1:0;
            if(num[cur][k]>=1<<(i)){
                res+=1<<i;
                cur=next[cur][1-k];
            }else{
                cur=next[cur][k];
            }
            if(cur==0)break; //这里是为了当进入到一个个数为0的分支,可以直接break
        }
        //return (x^end[cur]); 如果是求最大值
        return res;
    }
    int main()
    {
        int i,j,k,x,cur;
        int ans,m;
        //freopen("in.txt","r",stdin);
        while(~scanf("%d %d",&n,&m))
        {
            node=1;
            memset(next[0],0,sizeof(next[0]));
            for(i=0;i<n;i++)
            {
                scanf("%d",&x);
                if(vis[x])continue;
                vis[x]=1;
                v[i]=x;
                cur=0;
                for(j=19;j>=0;j--)
                {
                    k=((1<<j)&x)?1:0;
                    if(next[cur][k]==0)add(cur,k);
                    num[cur][k]++;
                    cur=next[cur][k];
                }
                end[cur]=x;
            }
            int t1,t2;
            t1=0;
            for(ans=i=0;i<m;i++){ //求最大值是max(ans,cal(v[i]))
                cin >> t2;
                t1^=t2;
                cout << cal(t1) << endl;
            }
        }
        return 0;
    }

    另外这题用线段树解也可以,即建600000个结点,每个叶子结点维护的就是元素中的集合,然后每次查询还是按01字典树找最小异或值那一套方法就行了

  • 相关阅读:
    深入正则表达式(0):正则表达式概述
    讲透学烂二叉树(二):图中树的定义&各类型树的特征分析
    讲透学烂二叉树(一):图的概念和定义—各种属性特征浅析
    Gzip之后继者Brotli浅析之CDN厂商的智能压缩,服务器Brotli设置
    ECMAScript进化史(1):​话说Web脚本语言王者JavaScript的加冕历史
    nginx网站限速限流配置——网站被频繁攻击,nginx上的设置limit_req和limit_conn
    linux添加用户,修改用户密码,修改用户权限,设置root用户操作
    nginx 限制ip访问,禁止非法域名指向本机ip——防止被别人绑定域名到自己IP的方法
    centos8 新增ssh自定义端口与屏蔽默认22端口。
    1g云主机升级centos8不满足centos 8 至少2g内存要求,linux虚拟内存来凑
  • 原文地址:https://www.cnblogs.com/zsben991126/p/10355237.html
Copyright © 2020-2023  润新知