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. 【Java每日一题】20161213

    package Dec2016; public class Ques1213 { public static void main(String[] args){ String str1 = " ...

  2. 用Spring Boot颠覆Java应用开发

    Java开发概述: 使用Java做Web应用开发已经有近20年的历史了,从最初的Servlet1.0一步步演化到现在如此多的框架,库以及整个生态系统.经过这么长时间的发展,Java作为一个成熟的语言, ...

  3. Java中,调试按钮的作用

    调试按钮的主要作用,找错 首先在我们需要找错位置的左边(数字前边)鼠标右键,点击切换断点 点击调试: 找到单步跳入按钮,即可实现步骤的单独运行,方便查找错误的原因, 查找结束记得将断点取消

  4. [moka同学笔记]七、Yii2.0课程笔记(魏曦老师教程)[新增管理员,重置密码]

  5. 除去String字符串里面指定的字符串

    主要用到String的两个方法,分别是subString(int len)或subString(int start,int end)和str.indexOf(String str1) 思路:先判断指定 ...

  6. Python学习基础知识概要

    1.输入输出 输出实例   1 2 print 'hello','world' hello world 输入实例   1 2 3 4 5 name = raw_input(); print " ...

  7. 对js中this的一点点理解

    1 当函数作为对象的方法被调用的时候 this就指向该对象 var o = { prop: 37, f: function() { return this.prop; } }; console.log ...

  8. 推荐15款最佳的 jQuery 分步引导插件

    当用户浏览到一个网站,它可能从不知道如何浏览,如何操作网站或 Web 应用程序的内容和流程.在这篇文章中,我们编制了一些最好的 jQuery 引导插件列表.你会发现这些插件对于提高你的网站的整体用户体 ...

  9. c++对象池使用

    // // ObjectPool.h // DragonBall // // Created by user on 13-8-22. // // #include <iostream> # ...

  10. js中constructor和prototype

    在最开始学习js的时候,我们在讲到原型链和构造函数的时候经常会有一个例子 如果我们定义函数如下: function Foo() { /* .. */ } Foo.prototype.bar = fun ...