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.废话不多说了,开 ...
随机推荐
- iOS SDK开发之 .framework静态库
查看.a静态库的生成及使用单击此处 注:这篇教程将只使用一小部分Objective-C代码,本文主要讲解从开始到应用的详细步骤.环境:xcode 9.2下面我们开始操作: 第一步:创建一个静态库工程 ...
- 在SOUI3中使用预编译XML
传统的XML文件通常是utf8编码的文本文件.使用文本文件好处在于方便查阅及修改. SOUI使用XML做为布局描述语言,所有的布局资源都是XML.文本文件格式自由,XML解析器需要对文件中的字符逐个解 ...
- eclipse添加约束文件
DTD 类型约束文件 1. Window->Preferences->XML->XML Catalog->User Specified Entries窗口中,选择Add ...
- CentOS7_装机软件推荐
目录 目录 前言 CentOS7中文支持 Install Yum Fastest Mirror Plugin Install Flash Player Install NTFS-3G Install ...
- Linux_SELinux使用
目录 目录 SELinux SElinux的应用 修改 SELinux 下次启动模式 修改 SELinux 上下文 上下文的快速模仿 SELinux布尔值 图形化管理SElinux SELinux错误 ...
- Unity UI —Text
Character Text 文本字体的编辑 Font Style 字体格式可以自行下载也可在windows自带字体中查找 Font Size 字体尺寸 Line Spacing 行距 Rich Te ...
- chrome 74 版本的chromedriver下载地址
微信扫二维码关注我的公众号,回复chromedriver 即可获取windows,liunx,mac版本最新selenium-chromedriver
- keras recall
# accuracy, fmeasure, precision,recall def mcor(y_true, y_pred): y_pred_pos = K.round(K.clip(y_pred, ...
- scala 使用case 关键字定义类不需要创建对象直接调用
1.必须是使用case 定义object类 package config import org.apache.spark.sql.SparkSession import org.apache.spar ...
- Go语言入门篇-基本流程控制
一.if语句 二.switch语句 三.for语句 四.select语句