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\n",tr.query(x)^x);
    while(m--){
        cin>>a;
        x^=a;
        printf("%d\n",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字典树找最小异或值那一套方法就行了

原文地址:https://www.cnblogs.com/zsben991126/p/10355237.html

时间: 2024-10-06 13:39:09

cf842D 01字典树|线段树 模板见hdu4825的相关文章

#树# #线段树#

线段树 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b].因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度. 模板: 建树 1 void Pushup(int rt){//根节点

HDU 4417 Super Mario ( 超级马里奥 + 主席树 + 线段树/树状数组离线处理 + 划分树 )

HDU 4417 - Super Mario ( 主席树 + 线段树/树状数组离线处理 + 划分树 ) 这道题有很多种做法,我先学习的是主席树.后面陆续补上线段树离线和划分树 题目大意就是给定一个区间给定一个数列,每次要求你查询区间[L,R]内不超过K的数的数量 主席树做法: 最基本的是静态第k大,这里是求静态的 <= K,差不多,在Query操作里面需要修改修改 先建立size棵主席树,然后询问的时候统计的是 第R棵主席树中[1,K]的数量 - 第L-1棵主席树中[1,K]的数量 注意这里下标

Hihocoder #1077 : RMQ问题再临-线段树(线段树:结构体建树+更新叶子往上+查询+巧妙使用father[]+线段树数组要开大4倍 *【模板】)

#1077 : RMQ问题再临-线段树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上回说到:小Hi给小Ho出了这样一道问题:假设整个货架上从左到右摆放了N种商品,并且依次标号为1到N,每次小Hi都给出一段区间[L, R],小Ho要做的是选出标号在这个区间内的所有商品重量最轻的一种,并且告诉小Hi这个商品的重量.但是在这个过程中,可能会因为其他人的各种行为,对 某些位置上的商品的重量产生改变(如更换了其他种类的商品). 小Ho提出了两种非常简单的方法,但是都不能

线段树--线段树【模板1】P3372

题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.求出某区间每一个数的和 输入格式 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k 操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和 输出格式 输出包含若干行整数,即为所有操作2

segment树(线段树)

线段树(segment tree)是一种Binary Search Tree或者叫做ordered binary tree.对于线段树中的每一个非叶子节点[a,b],它的左子树表示的区间为[a,(a+b)/2],右子树表示的区间为[(a+b)/2+1,b].如下图: [0-2] /       \ [0-1]          [2-2] /    \ [0-0]    [1-1] 下面看一道leetcode上的题,求动态区间的和(Range Sum Query - Mutable),题目如下:

[题解] bzoj 3600 没有人的算数 (替罪羊树+线段树)

- 传送门 - http://www.lydsy.com/JudgeOnline/problem.php?id=3600 - 思路 - 区间操作是线段树无疑,难点在于如何考虑这些数对的大小关系. 我们考虑一下平衡树,如果在平衡树中每个节点放一个数对,我们规定中序遍历前面的数对大于后面的,那么对于任意一个新数对(x,y)插入时只需知道x,y与每个节点的数对的关系,就可以在log的时间内放入. 对于x,y与某节点数对的关系,首先要知道x,y一定在平衡树中存在(否则怎么被用来构成新数对?),因此可以l

权值线段树&amp;&amp;线段树合并

权值线段树 所谓权值线段树,就是一种维护值而非下标的线段树,我个人倾向于称呼它为值域线段树. 举个栗子:对于一个给定的数组,普通线段树可以维护某个子数组中数的和,而权值线段树可以维护某个区间内数组元素出现的次数. 在实现上,由于值域范围通常较大,权值线段树会采用离散化或动态开点的策略优化空间. 更新操作: 更新的时候,我们向线段树中插入一个值v,那么所有包含v的区间值都需要+1.(每个节点维护对应区间中出现了多少个数) int update (long long v,long long l,lo

[CSP-S模拟测试]:树(树上LIS+主席树+线段树)

题目传送门(内部题78) 输入格式 第一行输入两个整数$n,q$,表示节点数和询问数. 第二行输入$n$个整数$w_i$,表示第$i$个点的智商. 第三行至第$n+1$行每行输入两个数$x,y$,表示树上一条边. 第$n+2$行至第$n+q+1$行每行三个数$u,v,c$表示一次探究.(保证$v$是$u$的祖先) 输出格式 输出$q$行,每行两个数表示探究过程中$cwystc$需要努力学习的次数. 样例 见下发文件 数据范围与提示 对于$10\%$的数据:$n\leqslant 1,000$ 对

BZOJ_3196_二逼平衡树(树套树:线段树+Treap)

描述 可以处理区间问题的平衡树. 分析 树套树.可以用线段树套Treap.人生第一道树套树的题... op1:如果在整区间,直接在该区间的treap上求解.否则分两个区间求解,然后相加.最后+1. op2:这个不太好直接做,可以二分,每次假定一个值,用这个值去做op1,以此求得一个rank=k+1的数,求rank=k的数等价与求这个数的前驱pre. op3:先删后加. op4&op5:如果在整区间,直接在该区间的treap上求解,否则分量个区间求解,pre取最大值,suc取最小值.注意有些数在有