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\n",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\n", 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

 

posted @ 2018-04-10 19:08  Aragaki  阅读(231)  评论(0编辑  收藏  举报