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

给你多个不同的数字问你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<<],R[MAXN<<],sum[MAXN<<];
int a[MAXN],T[MAXN],Hash[MAXN]; //T记录每个元素对应的根节点 //建树函数,建立一颗空树
int build(int l,int r)
{ //参数表示左右端点
int mid,root=++tol;
sum[root]=; //区间内数的个数为0
if(l<r)
{
mid=(l+r)>>;
L[root]=build(l,mid); //构造左子树并将左端点编号存入L
R[root]=build(mid+,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
if(l<r)
{
mid=(l+r)>>;
if(pos<=mid) L[root]=update(L[pre],l,mid,pos); //插入到左子树
else R[root]=update(R[pre],mid+,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)>>;
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+,r,k-num);
} int main()
{
int i,n,m,t,d,pos;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(i=;i<=n;i++)
{
scanf("%d",&a[i]);
Hash[i]=a[i];
}
sort(Hash+,Hash+n+);
d=unique(Hash+,Hash+n+)-Hash-; //d为不同数的个数
tol=; //编号初始化
T[]=build(,d); //1~d即区间
for(i=;i<=n;i++)
{ //实际上是对每个元素建立了一颗线段树,保存其根节点
pos=lower_bound(Hash+,Hash+d+,a[i])-Hash;
//pos就是当前数在原数组中排第pos
T[i]=update(T[i-],,d,pos);
}
int l,r,k;
while(m--)
{
scanf("%d%d%d",&l,&r,&k);
pos=query(T[l-],T[r],,d,k);
printf("%d\n",Hash[pos]);
}
}
return ;
}

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 << ];
int tol;
int num[MAXN], T[MAXN], idx[MAXN];
int build(int l, int r)
{
int mid, root = ++tol;
Tree[root].sum = ;
if (l < r)
{
mid = (l + r) >> ;
Tree[root].l = build(l, mid);
Tree[root].r = build(mid + , 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 + ;
if (l < r)
{
mid = (l + r) >> ;
if (pos <= mid)
{
Tree[root].l = update(Tree[pre].l, l, mid, pos);
}
else
{
Tree[root].r = update(Tree[pre].r, mid + , 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) >> ;
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 + , r, k - num);
}
}
int main()
{
int n, m, t, len, pos;
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &n, &m);
for (int i = ; i <= n; i++)
{
scanf("%d", &num[i]);
idx[i] = num[i];
}
sort(idx + , idx + n + );
len = unique(idx + , idx + n + ) - idx - ;
tol = ;
T[] = build(, len);
for (int i = ; i <= n; i++)
{
pos = lower_bound(idx + , idx + len + , num[i]) - idx;
T[i] = update(T[i - ], , len, pos);
}
int l, r, k;
while (m--)
{
scanf("%d%d%d", &l, &r, &k);
pos = query(T[l - ], T[r], , len, k);
printf("%d\n", idx[pos]);
}
}
return ;
}

给一个长为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 << ];
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) >> ;
Tree[root].l = build(l, mid);
Tree[root].r = build(mid + , 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) >> ;
if (pos <= mid)
{
Tree[root].l = update(Tree[pre].l, l, mid, pos, x);
}
else
{
Tree[root].r = update(Tree[pre].r, mid + , 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) >> ;
if (pos <= mid)
{
return query(Tree[ask].l, l, mid, pos);
}
else
{
return query(Tree[ask].r, mid + , r, pos);
}
}
int main()
{
int n, m, pos, x;
scanf("%d%d", &n, &m);
for (int i = ; i <= n; i++)
{
scanf("%d", &num[i]);
}
tol = ;
T[] = build(, n);
char ch;
while (m--)
{
cin >> ch >> pos >> x;
if (ch == 'Q')
{
cur++;
T[cur] = T[cur - ];
cout << query(T[x], , n, pos) << endl;
}
else if (ch == 'M')
{
cur++;
T[cur] = update(T[cur - ], , n, pos, x);
}
}
return ;
}

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

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

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

样例输入 

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


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 << ];
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 = ;
}
else if (l < r)
{
mid = (l + r) >> ;
Tree[root].l = build(l, mid);
Tree[root].r = build(mid + , 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) >> ;
if (pos <= mid)
{
Tree[root].l = update(Tree[pre].l, l, mid, pos, x);
}
else
{
Tree[root].r = update(Tree[pre].r, mid + , 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) >> ;
if (pos <= mid)
{
return query(Tree[ask].l, l, mid, pos);
}
else
{
return query(Tree[ask].r, mid + , r, pos);
}
}
int main()
{
int n, pos, x;
scanf("%d", &n);
tol = ;
T[] = build(, );
number[] = ;
char ch;
while (n--)
{
cin >> ch >> x;
if (ch == 'Q')
{
cout << query(T[cur], , , x) << endl;
}
else if (ch == 'A')
{
cur++;
T[cur] = update(T[cur - ], , , number[cur - ] + , x);
number[cur] = number[cur - ] + ;
}
else if (ch == 'U')
{
cur++;
T[cur] = T[cur - - x];
number[cur] = number[cur - - x];
}
}
return ;
}

ZX树初步的更多相关文章

  1. 线段树初步——转载自ljc20020730

    线段树初步   线段树模板1:https://www.luogu.org/problem/show?pid=3372 线段树模板2:https://www.luogu.org/problem/show ...

  2. 线段树初步&&lazy标记

    线段树 一.概述: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a, ...

  3. 主席树初步 HDU2665的区间第k小

    首先看一下这个人的blog吧,讲的精炼http://blog.sina.com.cn/s/blog_4a0c4e5d0101c8fr.html 然后再推荐一下这个人的blog:http://www.c ...

  4. 左偏树初步 bzoj2809 & bzoj4003

    看着百度文库学习了一个. 总的来说,左偏树这个可并堆满足 堆的性质 和 左偏 性质. bzoj2809: [Apio2012]dispatching 把每个忍者先放到节点上,然后从下往上合并,假设到了 ...

  5. 可持久化Trie树初步

    可持久化Trie树和可持久化线段树很像,依次插入信息,通过减法来进行历史版本查询. 2015年11月27日 bzoj3261 最大异或和 我们需要计算 a[p] xor a[p+1] xor ... ...

  6. 线段树初步__ZERO__.

    线段树,顾名思义,就是指一个个线段组成的树. 线段树的定义就是: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点.使用线段树可以快速的查找某 ...

  7. 主席树初步学习笔记(可持久化数组?静态区间第k大?)

    我接触 OI也快1年了,然而只写了3篇博客...(而且还是从DP跳到了主席树),不知道我这个机房吊车尾什么时候才能摸到大佬们的脚后跟orz... 前言:主席树这个东西,可以说是一种非常畸形的数据结构( ...

  8. 虚拟树研究-CheckBox初步判断只能在第一列

    //虚拟树研究-CheckBox初步判断只能在第一列 procedure TWindowsXPForm.XPTreeInitNode(Sender: TBaseVirtualTree; ParentN ...

  9. C#表达式树的初步了解

    在很早以前就听说过表达式树了,但并没有去了解它.虽然自己用过linq to sql和linq to entity,但也就用着就用着,并没有去深究c#代码怎么会生成sql代码而不是IL.废话不多说了,开 ...

随机推荐

  1. vmware fusion 找不到可以连接的有效对等进程

    红框会有什么提示 vmware...,你点击允许

  2. HDU4497 GCD and LCM(数论,质因子分解)

    HDU4497 GCD and LCM 如果 \(G \% L != 0\) ,那么输出 \(0\) . 否则我们有 \(L/G=(p_1^{r_1})\cdot(p_2^{r_2})\cdot(p_ ...

  3. sed扩展命令使用

    [root@b ~]# cat f.txt inet addr:192.168.0.110 Bcast:192.168.0.255 Mask:255.255.255.0[root@b ~]# cat ...

  4. Mybaits解决实体类字段与数据库字段不一致问题

    public class Employee { private Integer id; private String lastName; private String email; private S ...

  5. 树状数组的理解(前缀和 and 差分)

    二更—— 有神仙反映数星星那个题外链炸了,我决定把图给你们粘一下,汉语翻译的话在一本通提高篇的树状数组那一章里有,同时也修改了一些汉语语法的错误 这段时间学了线段树组,当神仙们都在学kmp和hash的 ...

  6. Python Module_os_操作系统

    目录 目录 前言 软件环境 os模块内建属性 osname 获取执行平台的类型 oslinesep 输出当前平台使用的行终止符 ossep 输出操作系统特定的路径分隔符 ospathsep 输出用于分 ...

  7. php5.4编译安装apache

    1.下载源码包 wget 网址/php-5.4.45.tar2.解压源码包 tar -zxvf php-5.4.45.tar3.创建一个安装目录 mkdir /usr/local/php4.进入解压后 ...

  8. Monkey测试:日志信息分析

    在跑monkey时,我们需要将日志输出到文件,然后对日志信息进行分析. 一.输出日志到文件 在monkey命令后加>文件地址 如:adb shell monkey 1000>E:/text ...

  9. gitlab+jenkins 搭建

    继前一篇gitlab,这一篇介绍jenkins搭建并与gitlab进行集成---这里不是详细的步骤 环境系统:centos 7.3 jenkins版本:jenkins-2.176.1-1.1.noar ...

  10. 【Spring】---【AOP】

    转发几篇文章 专治不会看源码的毛病--spring源码解析AOP篇 Spring3:AOP 理解AOP 什么是AOP? 转自: http://www.cnblogs.com/xiexj/p/73668 ...