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<<],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,表示撤销掉最后进行的若干次操作。
输出 对每一个询问操作单独输出一行,表示答案。
样例输入
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 << ];
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树初步的更多相关文章
- 线段树初步——转载自ljc20020730
线段树初步 线段树模板1:https://www.luogu.org/problem/show?pid=3372 线段树模板2:https://www.luogu.org/problem/show ...
- 线段树初步&&lazy标记
线段树 一.概述: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a, ...
- 主席树初步 HDU2665的区间第k小
首先看一下这个人的blog吧,讲的精炼http://blog.sina.com.cn/s/blog_4a0c4e5d0101c8fr.html 然后再推荐一下这个人的blog:http://www.c ...
- 左偏树初步 bzoj2809 & bzoj4003
看着百度文库学习了一个. 总的来说,左偏树这个可并堆满足 堆的性质 和 左偏 性质. bzoj2809: [Apio2012]dispatching 把每个忍者先放到节点上,然后从下往上合并,假设到了 ...
- 可持久化Trie树初步
可持久化Trie树和可持久化线段树很像,依次插入信息,通过减法来进行历史版本查询. 2015年11月27日 bzoj3261 最大异或和 我们需要计算 a[p] xor a[p+1] xor ... ...
- 线段树初步__ZERO__.
线段树,顾名思义,就是指一个个线段组成的树. 线段树的定义就是: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点.使用线段树可以快速的查找某 ...
- 主席树初步学习笔记(可持久化数组?静态区间第k大?)
我接触 OI也快1年了,然而只写了3篇博客...(而且还是从DP跳到了主席树),不知道我这个机房吊车尾什么时候才能摸到大佬们的脚后跟orz... 前言:主席树这个东西,可以说是一种非常畸形的数据结构( ...
- 虚拟树研究-CheckBox初步判断只能在第一列
//虚拟树研究-CheckBox初步判断只能在第一列 procedure TWindowsXPForm.XPTreeInitNode(Sender: TBaseVirtualTree; ParentN ...
- C#表达式树的初步了解
在很早以前就听说过表达式树了,但并没有去了解它.虽然自己用过linq to sql和linq to entity,但也就用着就用着,并没有去深究c#代码怎么会生成sql代码而不是IL.废话不多说了,开 ...
随机推荐
- 每日踩坑 2019-07-30 H5 使用 iframe 底部有白边
用个iframe累死累活的 用 js 动态计算高度, 结果明明px都对,然后却把页面滚动条也整出来了. 查看元素盒模型也一切正常. 然后仔细观察就发现是下边多了几个像素的白色边. 然后就 百度呗 以下 ...
- Mac ssh key生成
转载https://blog.csdn.net/wangjunling888/article/details/51115659 1. 查看秘钥是否存在 打开终端查看是否已经存在SSH密钥:cd ~/. ...
- nginx利用try_files实现多个源
比如一个视频网站,视频资源分散在几台机器上,但是给用要提供统一的IP,路径,端口.就需要nginx,先检查本机是否有改文件,如果没有就代理其他地方 location / { root /data/ww ...
- Mysql 创建函数出现This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary mys ...
- linux配置ssh公钥认证,打通root用户的免密码输入的scp通道
1.ssh-keygen ssh-keygen是unix-like系统的一个用来生成.管理ssh公钥和私钥的工具. 2.用法 常用的重要的选项有: -b num 指定生成多少比特长度的key,单位 ...
- HAProxy & Keepalived L4-L7 高可用负载均衡解决方案
目录 文章目录 目录 HAProxy 负载均衡器 应用特性 性能优势 会话保持 健康检查 配置文件 负载均衡策略 ACL 规则 Web 监控平台 Keepalived 虚拟路由器 核心组件 VRRP ...
- 测开之路一百四十六:WTForms之表单应用
WTForms主要是两个功能:1.生成HTML标签 2.对数据格式进行验证 官网:https://wtforms.readthedocs.io/en/stable/ 这篇介绍用wtform生成htm ...
- oracle-不完全数据库恢复-被动恢复-RMAN-06025/ORA-01190
不完全数据库恢复 到目前为止,前面讨论的都是完全恢复数据库,这是recover database\recover tablespace\recover datafile默认的行为特征. 所谓完全恢复指 ...
- ARTS-3
ARTS的初衷 Algorithm:主要是为了编程训练和学习.每周至少做一个 leetcode 的算法题(先从Easy开始,然后再Medium,最后才Hard).进行编程训练,如果不训练你看再多的算法 ...
- 用seborn的函数distplot(), jointplot(), pairplt()对数据的单变量分析绘图
1.用seaborn的distplot()函数绘制直方图.参数kde = True时会把分布曲线也画出来. 如下代码所示是绘制标准正态分布的分布图 import seaborn as sns impo ...