我惊奇的发现这两道题一模一样
题目背景
这是个非常经典的主席树入门题——静态区间第K小
数据已经过加强,请使用主席树。同时请注意常数优化
题目描述
如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。
输入输出格式
输入格式:
第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。
第二行包含N个整数,表示这个序列各项的数字。
接下来M行每行包含三个整数l, r, kl,r,k , 表示查询区间[l, r][l,r]内的第k小值。
输出格式:
输出包含k行,每行1个整数,依次表示每一次查询的结果
输入输出样例
说明
数据范围:
对于20%的数据满足:1 leq N, M leq 101≤N,M≤10
对于50%的数据满足:1 leq N, M leq 10^31≤N,M≤103
对于80%的数据满足:1 leq N, M leq 10^51≤N,M≤105
对于100%的数据满足:1 leq N, M leq 2cdot 10^51≤N,M≤2⋅105
对于数列中的所有数a_iai,均满足-{10}^9 leq a_i leq {10}^9−109≤ai≤109
样例数据说明:
N=5,数列长度为5,数列从第一项开始依次为[25957, 6405, 15770, 26287, 26465 ][25957,6405,15770,26287,26465]
第一次查询为[2, 2][2,2]区间内的第一小值,即为6405
第二次查询为[3, 4][3,4]区间内的第一小值,即为15770
第三次查询为[4, 5][4,5]区间内的第一小值,即为26287
第四次查询为[1, 2][1,2]区间内的第二小值,即为25957
第五次查询为[4, 4][4,4]区间内的第一小值,即为26287
解题思路:
定义:主席树是一种可持久化的线段树 又叫函数式线段树
刚开始学是不是觉得很蒙逼啊 其实我也是
主席树说简单了 就是保留你每一步操作完成之后的线段树 然后有可加减性
呃 。。。 这么说好像还是有点生涩
那么就拿poj2104来举例子吧 慢慢讲我觉得会很好的
题意就是给你一个100000长度的数字 然后100000次询问[L,R]之间第k大的数字是多少
这个很容易看出来 暴力根本不可以 黑你分分钟的事情啊
我们怎么办呢 想想线段树能不能做 想来想去 一颗线段树好像不能这么做 GG
那么我们做一个美好的假设:
我们建立100000棵美丽的线段树 每一个线段树的节点 表示这一个区间内有多少个数字
第一棵线段树保存着把第一个数字插入进去之后 每个区间有多少个数字
第二棵线段树保存着把第一个 第二个数字插入进去之后 每个区间有多少个数字
。
。
。
。
第n棵线段树保存着把第1,2,3。。。。n个数字插入进去之后 每个区间有多少个数字
好了 我们已经建立了这么多的线段树 我们接下来该怎么办呢?
对 就是查询
可是如何查询呢? 假设我们要查询[l,r]内的第k大
我们可以拿出第l-1 ,r 棵线段树,然后两者相减 我们想一下 这样不就得到了相当于插入了第l到r个点所建立的一棵线段树 这棵线段树每个节点保留的信息是:这个区间内数字的个数 然后我们往下二分查找 就可以得到第k大了
现在的问题时 这么庞大的空间开销我们耗费不起 我们该如何建立这样的线段树呢?
答案就是 我们要尽量利用重复节点
我们可以想一下 我们每次建立线段树 相对于前一棵线段树 我们只修改了它的一条路径 最多只有logn的变化 那么我们就存下这logn的变化 尽可能的利用重复节点 就可以达到重复使用的目的
//原文:https://blog.csdn.net/qq_34271269/article/details/54849370
AC代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #define maxn (int)(1e6+10) 5 6 using namespace std; 7 8 struct kkk { 9 int cnt,l,r; 10 }t[30*maxn]; 11 int init[maxn]; 12 int cop[maxn]; 13 int tc[maxn]; 14 int n,tcnt = 0,newn,m; 15 16 void Discretization() {//离散化 17 sort(init,init+n); 18 newn = unique(init,init+n) - init; 19 for(int i = 0;i < n; i++) cop[i] = lower_bound(init,init+n,cop[i]) - init; 20 } 21 22 int add(int num,int bei,int l,int r) { 23 ++tcnt; 24 t[tcnt].cnt = t[bei].cnt + 1; 25 int save = tcnt; 26 int mid = (l + r) >> 1; 27 if(l == r) { 28 return save; 29 } 30 else if(num <= mid) { 31 t[save].l = add(num,t[bei].l,l,mid); 32 t[save].r = t[bei].r; 33 } 34 else { 35 t[save].r = add(num,t[bei].r,mid + 1,r); 36 t[save].l = t[bei].l; 37 } 38 return save; 39 } 40 41 int chaxun(int x,int y,int k,int l,int r) { 42 if(l == r) return l; 43 int p = t[t[y].l].cnt - t[t[x].l].cnt; 44 int mid = (l + r) >> 1; 45 if(k <= p) 46 return chaxun(t[x].l,t[y].l,k,l,mid); 47 else 48 return chaxun(t[x].r,t[y].r,k-p,mid + 1,r); 49 } 50 51 int main() 52 { 53 scanf("%d%d",&n,&m); 54 for(int i = 0;i < n; i++) { 55 scanf("%d",&init[i]); 56 cop[i] = init[i]; 57 } 58 Discretization(); 59 for(int i = 1;i <= n; i++) { 60 int p = add(cop[i-1],tc[i-1],0,n); 61 tc[i] = p; 62 } 63 for(int i = 0;i < m; i++) { 64 int x,y,k; 65 scanf("%d%d%d",&x,&y,&k); 66 int ans = chaxun(tc[x-1],tc[y],k,0,n); 67 printf("%d ",init[ans]); 68 } 69 return 0; 70 }