POJ 2104 静态找区间第k大

静态区间第k大的问题,往往可以利用主席树来解决

这是主席树的第一道题

主席树大概可以理解为在n个节点上都建立一棵线段树,但是想想会超出内存

每一个节点保存的线段树都记录当前整段前缀区间的信息

但是因为每次添加后一个节点,那么他除了当前节点位置需要更新之外,其他的位置都可以保持跟上一棵节点对应的线段树一致,那么为了缩小内存,

将那些不需要改变的点的指针指向上一棵树对应的节点即可,其他多生成的节点也就是需要更新的节点,最多不超过log2n个,所以最后产生的线段树的

点的个数大概在nlogn的大致范围内

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <iostream>
 5 using namespace std;
 6 #define N 100005
 7 #define M int m=(l+r)>>1
 8 #define LS(o) node[o].ls
 9 #define RS(o) node[o].rs
10
11 int n , m , a[N] , b[N] , T[N];
12
13 struct Node{
14     int sz , ls , rs;
15     void init(){sz=0;ls=rs=0;}
16 }node[N*30];
17
18 int tot;
19
20 int build(int l , int r)
21 {
22     int u = tot++;
23     node[u].init();
24     if(l!=r){
25         M;
26         node[u].ls = build(l , m);
27         node[u].rs = build(m+1 , r);
28     }
29     return u;
30 }
31
32 void build(int o1 , int o2 , int l , int r , int pos)
33 {
34     node[o2].init();
35     node[o2].sz = node[o1].sz+1;
36     M;
37     if(l == r) return;
38     if(pos<=m){
39         node[o2].ls = tot++ , node[o2].rs = RS(o1);
40         build(LS(o1) , LS(o2) , l , m , pos);
41     }
42     else {
43         node[o2].rs = tot++ , node[o2].ls = LS(o1);
44         build(RS(o1) , RS(o2) , m+1 , r , pos);
45     }
46 }
47
48 int query(int o1 , int o2 , int l , int r , int k)
49 {
50     if(l==r) return l;
51     M;
52     int tmp;
53     if((tmp=node[LS(o2)].sz - node[LS(o1)].sz)>=k) return query(LS(o1) , LS(o2) , l , m , k);
54     else return query(RS(o1) , RS(o2) , m+1 , r , k-tmp);
55 }
56
57 int main()
58 {
59    // freopen("in.txt" , "r" , stdin);
60     while(~scanf("%d%d" , &n , &m)){
61         for(int i=1 ; i<=n ; i++)scanf("%d" , a+i);
62         for(int i=1 ; i<=n ; i++)b[i]=a[i];
63         sort(b+1 , b+n+1);
64         tot = 0;
65         T[0] = build(1 , n);
66         for(int i=1 ; i<=n ; i++){
67             int pos = lower_bound(b+1 , b+n+1 , a[i])-b;
68             T[i] = tot++;
69             build(T[i-1] , T[i] , 1 , n , pos);
70         }
71         while(m--){
72             int s , t , k;
73             scanf("%d%d%d" , &s , &t , &k);
74             int pos = query(T[s-1] , T[t] , 1 , n , k);
75             printf("%d\n" , b[pos]);
76         }
77     }
78     return 0;
79 }
时间: 2024-08-28 08:00:39

POJ 2104 静态找区间第k大的相关文章

POJ 2104 求序列里第K大 主席树裸体题

给定一个n的序列,有m个询问 每次询问求l-r 里面第k大的数字是什么 只有询问,没有修改 可以用归并树和划分树(我都没学过..囧) 我是专门冲着弄主席树来的 对主席树的建树方式有点了解了,不过这题为什么是在主席树里面这么操作的 还是有点不懂,今天照着模板敲了一遍就打多校了 再研究吧 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using name

POJ 2014.K-th Number 区间第k大 (归并树)

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 57543   Accepted: 19893 Case Time Limit: 2000MS Description You are working for Macrohard company in data structures department. After failing your previous task about key inse

POJ 2104 K-th Number(区间第k大数)(平方分割,归并树,划分树)

题目链接: http://poj.org/problem?id=2104 解题思路: 因为查询的个数m很大,朴素的求法无法在规定时间内求解.因此应该选用合理的方式维护数据来做到高效地查询. 如果x是第k个数,那么一定有 (1)在区间中不超过x的数不少于k个 (2)在区间中小于x的数有不到k个 因此,如果可以快速求出区间里不超过x的数的个数,就可以通过对x进行二分搜索来求出第k个数是多少. 接下来,我们来看一下如何计算在某个区间里不超过x个数的个数.如果不进行预处理,那么就只能遍历一遍所有元素.

POJ 2104 K-th Number(区间第k大数)(平方切割,归并树,划分树)

题目链接: http://poj.org/problem? id=2104 解题思路: 由于查询的个数m非常大.朴素的求法无法在规定时间内求解. 因此应该选用合理的方式维护数据来做到高效地查询. 假设x是第k个数,那么一定有 (1)在区间中不超过x的数不少于k个 (2)在区间中小于x的数有不到k个 因此.假设能够高速求出区间里不超过x的数的个数.就能够通过对x进行二分搜索来求出第k个数是多少. 接下来,我们来看一下怎样计算在某个区间里不超过x个数的个数. 假设不进行预处理,那么就仅仅能遍历一遍全

【POJ】【2104】区间第K大

可持久化线段树 可持久化线段树是一种神奇的数据结构,它跟我们原来常用的线段树不同,它每次更新是不更改原来数据的,而是新开节点,维护它的历史版本,实现“可持久化”.(当然视情况也会有需要修改的时候) 可持久化线段树的应用有很多,仅以区间第K大这种简单的问题来介绍这种数据结构. 我们原本建立的线段树是表示区间的,或者说,维护的是[位置],存的是每个位置上的各种信息.它的优点是满足区间加法,但不满足区间减法,所以我们这里要换一种建树方式:对于每个区间[1,i]建立一棵权值线段树.这个线段树的作用其实就

Poj 2104区间第k大(归并树)

题目链接 K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 36890 Accepted: 11860 Case Time Limit: 2000MS Description You are working for Macrohard company in data structures department. After failing your previous task about key ins

poj 2401 划分树 求区间第k大的数

题目:http://poj.org/problem?id=2104 划分树待我好好理解下再写个教程吧,觉得网上的内容一般,,, 模板题: 贴代码: #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define CLR(a) memset(a,0,sizeof(a)) const int MAXN = 1000

HDU3473--Minimum Sum(静态区间第k大)

Minimum Sum Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3047    Accepted Submission(s): 701 Problem Description You are given N positive integers, denoted as x0, x1 ... xN-1. Then give you

主席树(静态区间第k大)

前言 如果要求一些数中的第k大值,怎么做? 可以先就这些数离散化,用线段树记录每个数字出现了多少次. ... 那么考虑用类似的方法来求静态区间第k大. 原理 假设现在要有一些数 我们可以对于每个数都建一棵新的线段树,用来记录出现每个数字出现了多少次的前缀和. 那么假设要求区间[l,r]的第k大,将第r棵线段树减去第l-1棵线段树,像上面求所有数的第k大一样来求就可以了. 但是,对于每一个数都建一个线段树显然会爆空间. 现在考虑如何节约空间. 假设现在有四个数1 4 2 3,依次加入,可以这样处理