poj 2104 K-th Number(主席树)

主席树就是持久化的线段树,添加的时候,每更新了一个节点的线段树都被保存下来了。
查询区间[L,R]操作的时候,只需要用第R棵树减去第L-1棵树就是区间[L,R]中增加的元素对应的树,然后查询这棵两棵树的差值对应的树就可以达到我们的目的。
每增加一个节点,必然有一条边被改变,那条边上的所有节点都会被改变。除这条边之外的其它节点用的是上一棵树的。
K-th Number
Time Limit: 20000MS   Memory Limit: 65536K
Total Submissions: 62232   Accepted: 21860
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

添加的这个节点离散化之后是1,也就是最左下角的那个节点。

总共是7个数,离散化管它有几个不重复的数,按照最大情况7个个数来算就对了。

所以这7个数是依次会被放到这7个叶子节点上面的来。

数的序列是1 5 2 6 3 7 4

第一个数是1,离散化之后也是1,所以会被放到最左下角3那个位置,所以就是上图右边更新的情况,(注意看节点的下标)。

第5棵树根节点的左边有3个,第一棵树根节点左边只有一个,多3-1=2个,但是我要找的是3大的,所以必定在第五棵树减去第一颗树的右边的第一个。

测试代码(后面有AC代码)

 #include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = ;
const int N = MAXN*;
int n,m,q,tot;
int T[MAXN],A[MAXN],t[MAXN];
//详单于结构体
int lson[N],rson[N],sum[N];
vector<int>V;
int getid(int x) //离散化
{
return lower_bound(V.begin(),V.end(),x)-V.begin()+;
}
int build(int l,int r) //建立一棵空树
{
int rt = tot++;
sum[rt] = ;//初始化,相当于初始化结构体中的sum元素
//不是叶子节点
if(l!=r){
int mid=(l+r)>>;
lson[rt] = build(l,mid);
rson[rt] = build(mid+,r);
}
return rt;
} //比如说第一组数是(0,1),表示继承的第0棵树,然后插入的那个数的id是1
int update(int rt,int pos) //把数组中的元素一次加入新的线段树中
{
int nrt = tot++;//相当于节点13
int tmp = nrt;
//sum[13]=sum[0]+1,这是插入第一个点的情况,表示sum[13]第一棵树比 sum[0]第0棵树多了一个元素
sum[nrt] = sum[rt]+;
int l=,r=m;//相当于从根节点开始更新
while(l<r) {//不停的从上往下(二分)去更新到那条路径的叶子节点
int mid = (l+r)>>;
//插入节点在线段树的左边
if(pos<=mid) {
lson[nrt] = tot++;//左边的节点就是我们新创建的这个节点,相当于节点14
rson[nrt] = rson[rt];//右边节点就直接继承前一棵树 ,相当于节点8
nrt = lson[nrt];//让13节点向下走到14号节点
rt = lson[rt];//前一棵树继续往下走,把前一棵树0号节点的左孩子的值赋值给rt,方便让新的这棵树找得到5号节点,4号节点
r = mid;//二分
}else {
rson[nrt] = tot++;
lson[nrt] = lson[rt];//左边直接继承前一棵树
nrt = rson[nrt];
rt = rson[rt];
l=mid+;
}
sum[nrt] = sum[rt]+;//节点在前一棵树的基础上面个数+1
}
return tmp;
} //第y棵树减第x-1棵树就是xy之间的元素,然后找到第k大的即可
//printf("%d\n",V[query(T[x-1],T[y],k)-1]);
//相当于是在两棵树的差树的那颗树上面找
int query(int lrt,int rrt,int k)
{
int l=,r=m;//从根节点开始找
while(l<r) {//二分找
int mid = (l+r)>>;
int cnt = sum[lson[rrt]] - sum[lson[lrt]];//找到有多少个数目
if(cnt>=k) {
r = mid;
lrt = lson[lrt];
rrt = lson[rrt];
} else {
l = mid+;
k-=cnt;//跑到右边去就要把左边的减掉
lrt = rson[lrt];//第一棵树和第五课树都去右孩子,然后是找差值
rrt = rson[rrt];
}
}
return l;
}
//测试主席树
void print(){
cout<<"i"<<" "<<"lson[i]"<<" "<<"rson[i]"<<" "<<"sum[i]"<<" "<<endl;
for(int i=;i<=*n;i++){
cout<<i<<" "<<lson[i]<<" "<<rson[i]<<" "<<sum[i]<<" "<<endl;
}
}
//测试T数组
void printT(){
cout<<"i"<<" "<<"T[i]"<<" "<<endl;
for(int i=;i<=n;i++){
cout<<i<<" "<<T[i]<<" "<<endl;
}
}
int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
//n个数q个操作
scanf("%d%d",&n,&q);tot=;
//A[i]存那些数
for(int i=;i<=n;i++) {
scanf("%d",&A[i]);
V.push_back(A[i]);
}
sort(V.begin(),V.end());
V.erase(unique(V.begin(),V.end()),V.end());
//上面一部分是离散化
m=V.size();//找到不重复的元素的个数
cout<<m<<endl; T[] = build(,m);//建1到m的树
cout<<"tot: "<<tot<<endl;
print();
// cout<<T[0]<<endl;
printT();
for(int i=;i<=n;i++) {
T[i] = update(T[i-],getid(A[i]));
cout<<i<<" "<<getid(A[i])<<endl;
}
printT();
while(q--) {
int x,y,k;
scanf("%d%d%d",&x,&y,&k);
//第y棵树减第x-1棵树就是xy之间的元素,然后找到第k大的即可
//最后一个-1是因为V是从0开始
printf("%d\n",V[query(T[x-],T[y],k)-]);
}
print();
return ;
}

AC代码

 #include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = ;
const int N = MAXN*;
int n,m,q,tot;
int T[MAXN],A[MAXN],t[MAXN];
int lson[N],rson[N],sum[N];
vector<int>V;
int getid(int x) //离散化
{
return lower_bound(V.begin(),V.end(),x)-V.begin()+;
}
int build(int l,int r) //建立一棵空树
{
int rt = tot++;
sum[rt] = ;
if(l!=r){
int mid=(l+r)>>;
lson[rt] = build(l,mid);
rson[rt] = build(mid+,r);
}
return rt;
} int update(int rt,int pos) //把数组中的元素一次加入新的线段树中
{
int nrt = tot++;
int tmp = nrt;
sum[nrt] = sum[rt]+;
int l=,r=m;
while(l<r) {
int mid = (l+r)>>;
if(pos<=mid) {
lson[nrt] = tot++;
rson[nrt] = rson[rt];
nrt = lson[nrt];
rt = lson[rt];
r = mid;
}else {
rson[nrt] = tot++;
lson[nrt] = lson[rt];
nrt = rson[nrt];
rt = rson[rt];
l=mid+;
}
sum[nrt] = sum[rt]+;
}
return tmp;
} int query(int lrt,int rrt,int k)
{
int l=,r=m;
while(l<r) {
int mid = (l+r)>>;
int cnt = sum[lson[rrt]] - sum[lson[lrt]];
if(cnt>=k) {
r = mid;
lrt = lson[lrt];
rrt = lson[rrt];
} else {
l = mid+;
k-=cnt;
lrt = rson[lrt];
rrt = rson[rrt];
}
}
return l;
}
int main()
{//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&q);tot=;
for(int i=;i<=n;i++) {
scanf("%d",&A[i]);
V.push_back(A[i]);
}
sort(V.begin(),V.end());
V.erase(unique(V.begin(),V.end()),V.end());
m=V.size();
T[] = build(,m);
for(int i=;i<=n;i++) {
T[i] = update(T[i-],getid(A[i]));
}
while(q--) {
int x,y,k;
scanf("%d%d%d",&x,&y,&k);
printf("%d\n",V[query(T[x-],T[y],k)-]);
}
return ;
}

poj 2104 K-th Number(主席树,详细有用)的更多相关文章

  1. 【POJ 2104】 K-th Number 主席树模板题

    达神主席树讲解传送门:http://blog.csdn.net/dad3zz/article/details/50638026 2016-02-23:真的是模板题诶,主席树模板水过.今天新校网不好,没 ...

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

    POJ 2104为例(主席树入门题) 思想: 可持久化线段树,也叫作函数式线段树,也叫主席树(高大上). 可持久化数据结构(Persistent data structure):利用函数式编程的思想使 ...

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

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

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

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

  5. POJ 2104 K-th Number 主席树(区间第k大)

    题目链接: http://poj.org/problem?id=2104 K-th Number Time Limit: 20000MSMemory Limit: 65536K 问题描述 You ar ...

  6. POJ 2104 K-th Number ( 求取区间 K 大值 || 主席树 || 离线线段树)

    题意 : 给出一个含有 N 个数的序列,然后有 M 次问询,每次问询包含 ( L, R, K ) 要求你给出 L 到 R 这个区间的第 K 大是几 分析 : 求取区间 K 大值是个经典的问题,可以使用 ...

  7. POJ 2104:K-th Number(主席树静态区间k大)

    题目大意:对于一个序列,每次询问区间[l,r]的第k大树. 分析: 主席树模板题 program kthtree; type point=record l,r,s:longint; end; var ...

  8. SPOJ MKTHNUM & POJ 2104 - K-th Number - [主席树模板题]

    题目链接:http://poj.org/problem?id=2104 Description You are working for Macrohard company in data struct ...

  9. poj 2104 K-th Number(主席树 视频)

    K-th Number 题意: 给你一些数,让你求一个区间内,第k大的数是多少. 题解: 主席树第一题,看的qsc视频写的,戳戳戳 学到了unique函数,他的作用是:把相邻的重复的放到后面,返回值是 ...

  10. Poj 2104 K-th Number(主席树&&整体二分)

    K-th Number Time Limit: 20000MS Memory Limit: 65536K Case Time Limit: 2000MS Description You are wor ...

随机推荐

  1. [USACO] 奶牛零食 Treats for the Cows

    题目描述 约翰经常给产奶量高的奶牛发特殊津贴,于是很快奶牛们拥有了大笔不知该怎么花的钱.为此,约翰购置了N(1≤N≤2000)份美味的零食来卖给奶牛们.每天约翰售出一份零食.当然约翰希望这些零食全部售 ...

  2. <MySQL>入门六 变量

    /* 变量 系统变量: 全局变量 会话变量 自定义变量 用户变量 局部变量 */ -- ------------系统变量-------------------- /* 变量由系统提供,不是用户定义,属 ...

  3. centos7+VMware Workstation创建共享文件夹

    1.第一步设置宿主机共享文件夹路径 2.挂载VMware Tools,如下操作会将tools以光盘挂载点的方式进入到系统中. 3.centos7 挂载存有VMware Tools的光盘并进行安装 1) ...

  4. linux tail-在屏幕上显示指定文件的末尾若干行

    博主推荐:获取更多 linux文件内容查看命令 收藏:linux命令大全 tail命令用于输入文件中的尾部内容.tail命令默认在屏幕上显示指定文件的末尾10行.如果给定的文件不止一个,则在显示的每个 ...

  5. list tuple dict (列表,元祖,字典间的相互转换)

    #-*- coding:UTF-8 -*- #author:RXS002 #1.字典 dict = {'name':'Zara','age':7,'class':'First'} #字典转换为字符串, ...

  6. How Can You Tell the Difference Between LINQ Methods and Query Builder Methods?

    LINQ's method syntax looks very similar to the query builder methods,except for one big difference:t ...

  7. EF中避免查询重复执行的手段

    由于ef有lazyload机制,编写的查询语句往往都没有立即执行,当你轮训结果集的时候才会将查询翻译成database端的sql语句,执行sql将结果返回到方法中.但是,下次再使用前面的结果集的时候, ...

  8. App后台开发运维和架构实践学习总结(5)——App产品从需求到研发到开发到上线到产品迭代全过程

    前言 如果没有做过开发,研发过产品的人,很难体会做产品的艰难,刚进公司的人,一般充当的是程序开发,我这里说的是开发,它与研发是有区别的. 一个需求下来,如果不能很好地理解产品需求,如果不能很好的驾驭需 ...

  9. jQuery WeUI 组件下拉刷新和滚动加载的实现

    最近在做手机版使用到了下拉刷新和滚动加载,记录一下实现过程: 一.引入文件 ? 1 2 3 4 <link rel="stylesheet" href="Conte ...

  10. 仪仗队(codevs 2296)

    题目描述 Description 作为体育委员,C君负责这次运动会仪仗队的训练.仪仗队是由学生组成的N * N的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视线所及的学生人数来 ...