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

给你多个不同的数字问你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. iOS SDK开发之 .framework静态库

    查看.a静态库的生成及使用单击此处 注:这篇教程将只使用一小部分Objective-C代码,本文主要讲解从开始到应用的详细步骤.环境:xcode 9.2下面我们开始操作: 第一步:创建一个静态库工程 ...

  2. 在SOUI3中使用预编译XML

    传统的XML文件通常是utf8编码的文本文件.使用文本文件好处在于方便查阅及修改. SOUI使用XML做为布局描述语言,所有的布局资源都是XML.文本文件格式自由,XML解析器需要对文件中的字符逐个解 ...

  3. eclipse添加约束文件

    DTD 类型约束文件     1. Window->Preferences->XML->XML Catalog->User Specified Entries窗口中,选择Add ...

  4. CentOS7_装机软件推荐

    目录 目录 前言 CentOS7中文支持 Install Yum Fastest Mirror Plugin Install Flash Player Install NTFS-3G Install ...

  5. Linux_SELinux使用

    目录 目录 SELinux SElinux的应用 修改 SELinux 下次启动模式 修改 SELinux 上下文 上下文的快速模仿 SELinux布尔值 图形化管理SElinux SELinux错误 ...

  6. Unity UI —Text

    Character Text 文本字体的编辑 Font Style 字体格式可以自行下载也可在windows自带字体中查找 Font Size 字体尺寸 Line Spacing 行距 Rich Te ...

  7. chrome 74 版本的chromedriver下载地址

    微信扫二维码关注我的公众号,回复chromedriver 即可获取windows,liunx,mac版本最新selenium-chromedriver

  8. keras recall

    # accuracy, fmeasure, precision,recall def mcor(y_true, y_pred): y_pred_pos = K.round(K.clip(y_pred, ...

  9. scala 使用case 关键字定义类不需要创建对象直接调用

    1.必须是使用case 定义object类 package config import org.apache.spark.sql.SparkSession import org.apache.spar ...

  10. Go语言入门篇-基本流程控制

    一.if语句 二.switch语句 三.for语句 四.select语句