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

给你多个不同的数字问你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. Json C#解析

    介绍 项目中数据格式如果是是Json格式,推荐大家使用LitJson和Newtonsoft.json进行解析 库的详细介绍和下载地址 推荐使用VS自带的Nuget来使用 Newtonsoft.Json ...

  2. 监听浏览器返回键、后退、上一页事件(popstate)操作返回键

    在WebApp或浏览器中,会有点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面.确认离开页面或执行一些其它操作的需求.可以使用 popstate 事件进行监听返回.后退.上一页操作. 一 ...

  3. php5.4编译安装--nginx

    1.下载源码包 wget 网址/源码包2.解压源码包 tar -zxvf 源码包3.创建一个安装目录 mkdir /usr/local/php4.进入解压后的目录中,初始化安装环境./configur ...

  4. 阶段3 1.Mybatis_12.Mybatis注解开发_2 mybatis注解开发测试和使用注意事项

    新建测试类 这里使用了main方法进行测试 InputStream需要抛出异常 写完进行测试 测试结果 讲解 把第一天的IUserDao.xml文件复制到当前的工程里面 红色的取值要用的.黄色的是执行 ...

  5. 阶段3 1.Mybatis_11.Mybatis的缓存_4 mybatis一对多实现延迟加载

    改成单表查询 首先配置的是select.他需要配置的值是accountDao中的方法,查询所有的账户,但是必须有条件.根据用户的id column配置的是id.因为要用user表的id去关联查询 Ac ...

  6. Python学习之==>日志模块

    一.logging模块介绍 logging是Python中自带的标准模块,是Python中用来操作日志的模块. 1.控制台输出日志 import logging logging.basicConfig ...

  7. jupyter 服务器安装随笔

    python3:python3 -m pip install --upgrade pip python3 -m pip install jupyterpkg install py36-pyzmq-18 ...

  8. JavaScript —— 实现简单计算器【带有 开/关机 清零 退格 功能】

    <!doctype html> <html> <head> <meta charset="utf-8"> <meta name ...

  9. python下对mysql数据库的链接操作

    参考网址: https://blog.csdn.net/guofeng93/article/details/53994112 https://blog.csdn.net/Chen_Eris/artic ...

  10. Mysql-使用xtrabackup添加Slave

    1.备份主库数据(主库操作) (1)安装innobackupex # yum -y install http://www.percona.com/downloads/percona-release/r ...