K-th Number
Time Limit: 20000MS   Memory Limit: 65536K
Total Submissions: 51440   Accepted: 17594
Case Time Limit: 2000MS

Description

You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment. 
That is, given an array a[1...n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: "What would be the k-th number in a[i...j] segment, if this segment was sorted?" 
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2...5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5. 

Input

The first line of the input file contains n --- the size of the array, and m --- the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000). 
The second line contains n different integer numbers not exceeding 109 by their absolute values --- the array for which the answers should be given. 
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k). 

Output

For each question output the answer to it --- the k-th number in sorted a[i...j] segment. 

Sample Input

7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3

Sample Output

5
6
3

Hint

This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.

Source

Northeastern Europe 2004, Northern Subregion

document:

1.http://blog.csdn.net/metalseed/article/details/8045038

2.http://www.cnblogs.com/oyking/p/3230296.html

 
解决区间极值查询问题
主席树就是对每个前缀做了一个线段树
这样相当于处理了前缀和
提取一段区间,就是T[j]-T[i-1]
每棵线段树都按照[1,n]建树(保证链可以共用)
离散化后按照序列的顺序建树
T[i+1]和T[i]只有一条链不同,其他的可以共用
 
插入操作就是只把新链上的节点新建,其他的都用上一课树的
查询操作就是一个kth的过程,只不过要用两棵树的差值
 
PS:
1.两种建树写法,貌似用引用比较快
2.每个数字不同,离散化排序就行了
 
[2016-12-31]一些新理解
就是可持久化线段树,对于每个版本建立一颗线段树,可以查询历史版本
为了节省内存和时间直接使用历史版本的形态,把修改的地方沿途新开节点,其他地方继承历史版本
主席树就是前缀和套线段树,每个前缀和建立一颗线段树,继承上一个历史版本,只是单点修改
区间修改也一样,所有区间修改到的点都要新开节点 标记下放时也要新开
总结:就是把各种操作修改到(包括因为下方标记而修改)的节点新开节点,写法上就是多了新开节点而已,其他一样
主席树的线段树是值域线段树,线段树的形态固定并且维护的信息是出现次数所以是可减的,那么主席树做差就得到了一个区间的值域线段树,可以在线段树上二分求kth
 
主席树的空间实际<理论

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+;
int read(){
char c=getchar();int x=,f=;
while(c<''||c>''){if(c=='-')f=-; c=getchar();}
while(c>=''&&c<=''){x=x*+c-''; c=getchar();}
return x*f;
}
int n,m,mp[N],l,r,k;
struct data{
int v,id;
bool operator <(const data &r)const{return v<r.v;}
}a[N];
struct node{
int lc,rc,size;
}t[N*];
int cnt=,root[N];
void insert(int num,int &x,int l,int r){//printf("ins %d %d %d\n",l,r,x);
cnt++;
t[cnt]=t[x];x=cnt;
++t[x].size;
if(l==r) return;
int mid=(l+r)>>;
if(num<=mid) insert(num,t[x].lc,l,mid);
else insert(num,t[x].rc,mid+,r);
}
int ins(int num,int pre,int l,int r){
int x=++cnt;
t[x]=t[pre]; ++t[x].size;
if(l==r) return x;
int mid=(l+r)>>;
if(num<=mid) t[x].lc=ins(num,t[x].lc,l,mid);
else t[x].rc=ins(num,t[x].rc,mid+,r);
return x;
}
int query(int i,int j,int l,int r,int k){
if(l==r) return l;
int ls=t[t[j].lc].size-t[t[i].lc].size;
int mid=(l+r)>>;
if(k<=ls) return query(t[i].lc,t[j].lc,l,mid,k);
else return query(t[i].rc,t[j].rc,mid+,r,k-ls);
} int main(){
n=read();m=read();
for(int i=;i<=n;i++) a[i].v=read(),a[i].id=i;
sort(a+,a++n);
for(int i=;i<=n;i++) mp[a[i].id]=i; for(int i=;i<=n;i++){
//root[i]=ins(mp[i],root[i-1],1,n);
root[i]=root[i-];
insert(mp[i],root[i],,n);
}
while(m--){
l=read();r=read();k=read();
printf("%d\n",a[query(root[l-],root[r],,n,k)].v); }
}

[2017-03-02]

在扔上一点课件上的东西,虽然感觉跟前面的总结有点重复,但还是有点用吧

函数式编程:不修改,只新增(保留所有的历史版本)。
@ 考虑一次单点修改对整棵树的信息的影响;只有一条链上的
信息真正改变了。
@ 函数式线段树:对于线段树所有的单点修改操作,不真正的
修改,而是通过新增节点的方式来构建。

由于除了链上的信息是不变的,所以把直接指向它们就可以
了。
@ 所以整棵线段树是动态的,要用动态的节点来实现。
@ 只需要记录根节点,就能访问“第x 次修改后的线段树”
了。

新模板:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lc(x) t[x].lc
#define rc(x) t[x].rc
typedef long long ll;
const int N=1e5+;
int read(){
char c=getchar();int x=,f=;
while(c<''||c>''){if(c=='-')f=-; c=getchar();}
while(c>=''&&c<=''){x=x*+c-''; c=getchar();}
return x*f;
}
int n,m,mp[N],l,r,k;
struct Fode{
int v,id;
bool operator <(const data &r)const{return v<r.v;}
}a[N];
struct node{
int lc,rc,size;
}t[N*];
int sz,root[N];
void fIns(int &x,int l,int r,int p){
t[++sz]=t[x];x=sz;
t[x].size++;
if(l==r) return;
int mid=(l+r)>>;
if(p<=mid) fIns(t[x].lc,l,mid,p);
else fIns(t[x].rc,mid+,r,p);
}
int fQue(int x,int y,int l,int r,int k){
if(l==r) return l;
int lsize=t[lc(y)].size-t[lc(x)].size;
int mid=(l+r)>>;
if(k<=lsize) return fQue(lc(x),lc(y),l,mid,k);
else return fQue(rc(x),rc(y),mid+,r,k-lsize);
} int main(){
freopen("in","r",stdin);
n=read();m=read();
for(int i=;i<=n;i++) a[i].v=read(),a[i].id=i;
sort(a+,a++n);
for(int i=;i<=n;i++) mp[a[i].id]=i; for(int i=;i<=n;i++) root[i]=root[i-],fIns(root[i],,n,mp[i]);
while(m--){
l=read();r=read();k=read();
printf("%d\n",a[fQue(root[l-],root[r],,n,k)].v);
}
}
 
 

POJ2104 K-th Number[主席树]【学习笔记】的更多相关文章

  1. 主席树学习笔记(静态区间第k大)

    题目背景 这是个非常经典的主席树入门题——静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输出 ...

  2. 主席树学习笔记-hdu-2665

    主席树就是对每个历史版本都建了一颗线段树,这样我们在统计一些问题的时候,对于一个区间[L,R]的询问,就可以利用前缀和的思想找到第L-1和第R颗历史版本的线段树来处理查找.由于这样空间需求就增大了,注 ...

  3. 【poj2104】K-th Number 主席树

    题目描述 You are working for Macrohard company in data structures department. After failing your previou ...

  4. poj2104 k-th number 主席树入门讲解

    poj2104 k-th number 主席树入门讲解 定义:主席树是一种可持久化的线段树 又叫函数式线段树   刚开始学是不是觉得很蒙逼啊 其实我也是 主席树说简单了 就是 保留你每一步操作完成之后 ...

  5. zkw线段树学习笔记

    zkw线段树学习笔记 今天模拟赛线段树被卡常了,由于我自带常数 \(buff\),所以学了下zkw线段树. 平常的线段树无论是修改还是查询,都是从根开始递归找到区间的,而zkw线段树直接从叶子结点开始 ...

  6. poj 2104 K-th Number 主席树+超级详细解释

    poj 2104 K-th Number 主席树+超级详细解释 传送门:K-th Number 题目大意:给出一段数列,让你求[L,R]区间内第几大的数字! 在这里先介绍一下主席树! 如果想了解什么是 ...

  7. 仙人掌&圆方树学习笔记

    仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...

  8. 线段树学习笔记(基础&进阶)(一) | P3372 【模板】线段树 1 题解

    什么是线段树 线段树是一棵二叉树,每个结点存储需维护的信息,一般用于处理区间最值.区间和等问题. 线段树的用处 对编号连续的一些点进行修改或者统计操作,修改和统计的复杂度都是 O(log n). 基础 ...

  9. [POJ2104] K – th Number (可持久化线段树 主席树)

    题目背景 这是个非常经典的主席树入门题--静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输 ...

随机推荐

  1. postgreSQL时间、日期函数

    一.获取系统时间函数 1.1.获取当前完整时间 select now(); select current_timestamp; 1.2.获取当前日期 select currnt_date: 1.3.获 ...

  2. stm32 usart 异步传输示例

    STM32F103xE的USART异步数据传输示例 USART全称Universal Synchronous/Asynchronous Receiver/Transmitter,是一种可以进行同步/异 ...

  3. python征程1.1(初识python)

    在学习python前必须要掌握的一些基本知识   1.编程语言 2.python   .  C#   JAVA 3.python:  pypy    cpython     jpython 4.执行方 ...

  4. 从零开始学 Java - Spring AOP 实现用户权限验证

    每个项目都会有权限管理系统 无论你是一个简单的企业站,还是一个复杂到爆的平台级项目,都会涉及到用户登录.权限管理这些必不可少的业务逻辑.有人说,企业站需要什么权限管理阿?那行吧,你那可能叫静态页面,就 ...

  5. JMeter专题系列(四)参数化

    JMeter也有像LR中的参数化,本篇就来介绍下JMeter的参数化如何去实现. 参数化:录制脚本中有登录操作,需要输入用户名和密码,假如系统不允许相同的用户名和密码同时登录,或者想更好的模拟多个用户 ...

  6. 常见.NET功能代码汇总 (2)

    常见.NET功能代码汇总 23,获取和设置分级缓存 获取缓存:首先从本地缓存获取,如果没有,再去读取分布式缓存写缓存:同时写本地缓存和分布式缓存 private static T GetGradeCa ...

  7. 精心挑选10款优秀的 jQuery 图片左右滚动插件

    在现代的网页设计中,图片和内容滑块是一种极为常见和重要的元素.你可以从头开始编写自己的滑动效果,但是这将浪费很多时间,因为网络上已经有众多的优秀的 jQuery 滑块插件.当然,如果要从大量的 jQu ...

  8. AMD and CMD are dead之KMD.js之懒

    缘由 "懒"在软件设计中,有着重大的意义.最常见的两种"懒",便是: 懒得计算 懒得加载 "懒得计算"常见于服务器端: 比如Multipla ...

  9. React入门最好的学习实例-TodoList

    前言 React 的核心思想是:封装组件,各个组件维护自己的状态和 UI,当状态变更,自动重新渲染整个组件. 最近前端界闹的沸沸扬扬的技术当属react了,加上项目需要等等原因,自己也决定花些时间来好 ...

  10. Atitit。数据库 安全性 重要敏感数据加密存储解决方案

    Atitit.数据库 安全性 重要敏感数据加密存储解决方案 1.1. 加密存储的重要性1 1.2. 使用的加密算法aes1 1.3. 数据加密以后会有一些问题.1 1.3.1. 一个是统计,比如统计资 ...