zoukankan      html  css  js  c++  java
  • ZX树初步

    一些数据结构,比如线段树或平衡树,他们一般是要么维护每个元素在原序列中的排列顺序,要么是维护每个元素的大小顺序,若是像二者兼得那么就想想主席树.

    给你多个不同的数字问你1-N第K大的数是多少 最简单的方法是直接排序然后输出第K个

    但是如果用线段树做的话该怎么做

    我们可以先离散化这N个数 然后把线段树底部第i个节点的值设为第i大的值出现了多少次 然后pushup rt[i]=rt[i<<1]+rt[i<<1|1]

    这样我们从线段树的根节点二分就可以知道答案了

    主席树就是有N个节点 每个节点是一棵arr[i]插入后的线段树

    因为修改一个底部节点只更改LOGN个节点 所以空间复杂度变得可以接受

    当静态询问L-R区间第K大的数时 我们只要在第R个线段树减去第L-1个线段树的差线段树上二分寻找就可以了

    动态询问的话 需要树套树 在外面再套个树状数组解决

    代码解析

    #include<stdio.h>  
    #include<string.h>  
    #include<iostream>  
    #include<algorithm>  
    #define MAXN 100010  
    using namespace std;  
      
    int tol;  
    //若tol值相同,则L、R、sum就表示同一个节点  
    //L为左端点的编号,R为右端点的编号,sum表示区间[L,R]内数的个数   
    int L[MAXN<<5],R[MAXN<<5],sum[MAXN<<5];  
    int a[MAXN],T[MAXN],Hash[MAXN]; //T记录每个元素对应的根节点   
      
    //建树函数,建立一颗空树  
    int build(int l,int r)  
    { //参数表示左右端点   
        int mid,root=++tol;  
        sum[root]=0; //区间内数的个数为0  
        if(l<r)  
        {  
            mid=(l+r)>>1;  
            L[root]=build(l,mid);   //构造左子树并将左端点编号存入L   
            R[root]=build(mid+1,r); //构造右子树并将右端点编号存入R  
        }  
        return root;  
    }  
      
    //更新函数  
    int update(int pre,int l,int r,int pos)  
    {//参数分别为:上一线段树的根节点编号,左右端点,插入数在原数组中排第pos   
        //从根节点往下更新到叶子,新建立出一路更新的节点,这样就是一颗新树了。  
        int mid,root=++tol;  
        L[root]=L[pre]; //先让其等于前面一颗树   
        R[root]=R[pre]; //先让其等于前面一颗树  
        sum[root]=sum[pre]+1; //当前节点一定被修改,数的个数+1   
        if(l<r)  
        {  
            mid=(l+r)>>1;  
            if(pos<=mid) L[root]=update(L[pre],l,mid,pos); //插入到左子树   
            else R[root]=update(R[pre],mid+1,r,pos); //插入到右子树   
        }  
        return root;  
    }  
      
    //查询函数,返回的是第k大的数在原数组中排第几  
    int query(int u,int v,int l,int r,int k)  
    { //参数分别为:两颗线段树根节点的编号,左右端点,第k大   
        //只会查询到相关的节点   
        int mid,num;  
        if(l>=r) return l;  
        mid=(l+r)>>1;  
        num=sum[L[v]]-sum[L[u]]; //当前询问的区间中左子树中的元素个数  
        //如果左儿子中的个数大于k,则要查询的值在左子树中   
        if(num>=k) return query(L[u],L[v],l,mid,k);   
        //否则在右子树中   
        else return query(R[u],R[v],mid+1,r,k-num);  
    }  
      
    int main()  
    {  
        int i,n,m,t,d,pos;  
        scanf("%d",&t);  
        while(t--)  
        {  
            scanf("%d%d",&n,&m);  
            for(i=1;i<=n;i++)  
            {  
                scanf("%d",&a[i]);  
                Hash[i]=a[i];  
            }  
            sort(Hash+1,Hash+n+1);  
            d=unique(Hash+1,Hash+n+1)-Hash-1; //d为不同数的个数  
            tol=0; //编号初始化   
            T[0]=build(1,d); //1~d即区间   
            for(i=1;i<=n;i++)  
            { //实际上是对每个元素建立了一颗线段树,保存其根节点  
                pos=lower_bound(Hash+1,Hash+d+1,a[i])-Hash;  
                //pos就是当前数在原数组中排第pos   
                T[i]=update(T[i-1],1,d,pos);  
            }  
            int l,r,k;  
            while(m--)  
            {  
                scanf("%d%d%d",&l,&r,&k);  
                pos=query(T[l-1],T[r],1,d,k);  
                printf("%d
    ",Hash[pos]);  
            }  
        }  
        return 0;  
    }  
    View Code

     POJ 2104 结构体版

    #include<stdio.h>
    #include<string.h>
    #include<iostream>
    #include<algorithm>
    #define MAXN 100010
    using namespace std;
    struct ZXS
    {
            int l, r;
            int sum;
    } Tree[MAXN << 5];
    int tol;
    int num[MAXN], T[MAXN], idx[MAXN];
    int build(int l, int r)
    {
            int mid, root = ++tol;
            Tree[root].sum = 0;
            if (l < r)
            {
                    mid = (l + r) >> 1;
                    Tree[root].l = build(l, mid);
                    Tree[root].r = build(mid + 1, r);
            }
            return root;
    }
    int update(int pre, int l, int r, int pos)
    {
            int mid, root = ++tol;
            Tree[root].l = Tree[pre].l;
            Tree[root].r = Tree[pre].r;
            Tree[root].sum = Tree[pre].sum + 1;
            if (l < r)
            {
                    mid = (l + r) >> 1;
                    if (pos <= mid)
                    {
                            Tree[root].l = update(Tree[pre].l, l, mid, pos);
                    }
                    else
                    {
                            Tree[root].r = update(Tree[pre].r, mid + 1, r, pos);
                    }
            }
            return root;
    }
    int query(int askl, int askr, int l, int r, int k)
    {
            int mid, num;
            if (l >= r)
            {
                    return l;
            }
            mid = (l + r) >> 1;
            num = Tree[Tree[askr].l].sum - Tree[Tree[askl].l].sum;
            if (num >= k)
            {
                    return query(Tree[askl].l, Tree[askr].l, l, mid, k);
            }
            else
            {
                    return query(Tree[askl].r, Tree[askr].r, mid + 1, r, k - num);
            }
    }
    int main()
    {
            int n, m, t, len, pos;
            scanf("%d", &t);
            while (t--)
            {
                    scanf("%d%d", &n, &m);
                    for (int i = 1; i <= n; i++)
                    {
                            scanf("%d", &num[i]);
                            idx[i] = num[i];
                    }
                    sort(idx + 1, idx + n + 1);
                    len = unique(idx + 1, idx + n + 1) - idx - 1;
                    tol = 0;
                    T[0] = build(1, len);
                    for (int i = 1; i <= n; i++)
                    {
                            pos = lower_bound(idx + 1, idx + len + 1, num[i]) - idx;
                            T[i] = update(T[i - 1], 1, len, pos);
                    }
                    int l, r, k;
                    while (m--)
                    {
                            scanf("%d%d%d", &l, &r, &k);
                            pos = query(T[l - 1], T[r], 1, len, k);
                            printf("%d
    ", idx[pos]);
                    }
            }
            return 0;
    }
    View Code

    给一个长为N的数列,有M次操作,每次操作是以下两种之一: (1)修改数列中的一个数 (2)求数列中某位置在某次操作后的值

     输入 第一行两个正整数N和M。 第二行N个整数表示这个数列。 
     接下来M行,每行开头是一个字符,若该字符为’M’,则表示一个修改操作,接下来两个整数x和y,表示把x位置的值修改为y;若该字符为’Q’,则表示一个询问操作,接 下来两个整数x和y,表示求x位置在第y次操作后的值。 
     输出 对每一个询问操作单独输出一行,表示答案。

     样例输入 
     5 3 
     1 2 3 4 5 
     Q 1 0 
     M 1 3 
     Q 1 2 
     样例输出 
     1 
     3 
     提示 
     1<=N<=10^5,1<=M<=10^5

    #include<stdio.h>
    #include<string.h>
    #include<iostream>
    #include<algorithm>
    #define MAXN 100010
    using namespace std;
    struct ZXS
    {
            int l, r;
            int value;
    } Tree[MAXN << 5];
    int tol, cur;
    int num[MAXN], T[MAXN], idx[MAXN];
    int build(int l, int r)
    {
            int mid, root = ++tol;
            if (l == r)
            {
                    Tree[root].value = num[l];
            }
            else if (l < r)
            {
                    mid = (l + r) >> 1;
                    Tree[root].l = build(l, mid);
                    Tree[root].r = build(mid + 1, r);
            }
            return root;
    }
    int update(int pre, int l, int r, int pos, int x)
    {
            int mid, root = ++tol;
            Tree[root].l = Tree[pre].l;
            Tree[root].r = Tree[pre].r;
            if (l == r)
            {
                    Tree[root].value = x;
            }
            else if (l < r)
            {
                    mid = (l + r) >> 1;
                    if (pos <= mid)
                    {
                            Tree[root].l = update(Tree[pre].l, l, mid, pos, x);
                    }
                    else
                    {
                            Tree[root].r = update(Tree[pre].r, mid + 1, r, pos, x);
                    }
            }
            return root;
    }
    int query(int ask, int l, int r, int pos)
    {
            int mid, num;
            if (l >= r)
            {
                    return Tree[ask].value;
            }
            mid = (l + r) >> 1;
            if (pos <= mid)
            {
                    return query(Tree[ask].l, l, mid, pos);
            }
            else
            {
                    return query(Tree[ask].r, mid + 1, r, pos);
            }
    }
    int main()
    {
            int n, m, pos, x;
            scanf("%d%d", &n, &m);
            for (int i = 1; i <= n; i++)
            {
                    scanf("%d", &num[i]);
            }
            tol = 0;
            T[0] = build(1, n);
            char ch;
            while (m--)
            {
                    cin >> ch >> pos >> x;
                    if (ch == 'Q')
                    {
                            cur++;
                            T[cur] = T[cur - 1];
                            cout << query(T[x], 1, n, pos) << endl;
                    }
                    else if (ch == 'M')
                    {
                            cur++;
                            T[cur] = update(T[cur - 1], 1, n, pos, x);
                    }
            }
            return 0;
    }
    View Code

     描述 给一个空数列,有M次操作,每次操作是以下三种之一: (1)在数列后加一个数 (2)求数列中某位置的值 

    撤销掉最后进行的若干次操作(1和3)

    输入 第一行一个正整数M。 
    接下来M行,每行开头是一个字符,若该字符为’A’,则表示一个加数操作,接下来一个整数x,表示在数列后加一个整数x;若该字符为’Q’,则表示一个询问操作,接下来一个整数x,表示求x位置的值;若该字符为’U’,则表示一个撤销操作,接下来一个整数x,表示撤销掉最后进行的若干次操作。 
    输出 对每一个询问操作单独输出一行,表示答案。

    样例输入 
    9 
    A 1 
    A 2 
    A 3 
    Q 3 
    U 1 
    A 4 
    Q 3 
    U 2 
    Q 3 
    样例输出 
    3 
    4 
    3

    提示 1<=M<=10^5

    #include<stdio.h>
    #include<string.h>
    #include<iostream>
    #include<algorithm>
    #define MAXN 100010
    using namespace std;
    struct ZXS
    {
            int l, r;
            int value;
    } Tree[MAXN << 5];
    int tol, cur;
    int num[MAXN], T[MAXN], idx[MAXN];
    int number[MAXN];
    int build(int l, int r)
    {
            int mid, root = ++tol;
            if (l == r)
            {
                    Tree[root].value = 0;
            }
            else if (l < r)
            {
                    mid = (l + r) >> 1;
                    Tree[root].l = build(l, mid);
                    Tree[root].r = build(mid + 1, r);
            }
            return root;
    }
    int update(int pre, int l, int r, int pos, int x)
    {
            int mid, root = ++tol;
            Tree[root].l = Tree[pre].l;
            Tree[root].r = Tree[pre].r;
            if (l == r)
            {
                    Tree[root].value = x;
            }
            else if (l < r)
            {
                    mid = (l + r) >> 1;
                    if (pos <= mid)
                    {
                            Tree[root].l = update(Tree[pre].l, l, mid, pos, x);
                    }
                    else
                    {
                            Tree[root].r = update(Tree[pre].r, mid + 1, r, pos, x);
                    }
            }
            return root;
    }
    int query(int ask, int l, int r, int pos)
    {
            int mid, num;
            if (l >= r)
            {
                    return Tree[ask].value;
            }
            mid = (l + r) >> 1;
            if (pos <= mid)
            {
                    return query(Tree[ask].l, l, mid, pos);
            }
            else
            {
                    return query(Tree[ask].r, mid + 1, r, pos);
            }
    }
    int main()
    {
            int n, pos, x;
            scanf("%d", &n);
            tol = 0;
            T[0] = build(1, 100000);
            number[0] = 0;
            char ch;
            while (n--)
            {
                    cin >> ch >> x;
                    if (ch == 'Q')
                    {
                            cout << query(T[cur], 1, 100000, x) << endl;
                    }
                    else if (ch == 'A')
                    {
                            cur++;
                            T[cur] = update(T[cur - 1], 1, 100000, number[cur - 1] + 1, x);
                            number[cur] = number[cur - 1] + 1;
                    }
                    else if (ch == 'U')
                    {
                            cur++;
                            T[cur] = T[cur - 1 - x];
                            number[cur] = number[cur - 1 - x];
                    }
            }
            return 0;
    }
    View Code
  • 相关阅读:
    Linux下如何从mysql数据库里导出导入数据
    安装好Pycharm后如何配置Python解释器简易教程
    Windows离线安装Python第三方库的方法
    时间输入框的测试方法
    doc转html
    pdf转png图片
    html转pdf
    html转pdf
    复习 注解反射
    Mybatis实现插入数据的时候将主键赋值给对象的两种方法
  • 原文地址:https://www.cnblogs.com/Aragaki/p/8781656.html
Copyright © 2011-2022 走看看