POJ 2104 K-th Number(主席树——附讲解)
Description
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 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
题目大意:给一串数字,多次询问区间的第k小值
思路:不带修改的主席树
附资料:http://blog.csdn.net/metalseed/article/details/8045038
http://www.abandonzhang.com/archives/29
——————————————————————————————————————————————————————————————————————————————————————————————
以下转自http://prominences.weebly.com/1/post/2013/02/1.html
可持久化线段树,也叫作函数式线段树,也就是主席树,(。。。因为先驱就是fotile主席。。Orz。。。)
网上的教程很少啊,有的教程写得特别简单,4行中文,然后就是一篇代码~~
这里,我将从查找区间第k小值(不带修改)题的可持久化线段树做法中,讲一讲主席树。
/*只是略懂,若有错误,还请多多包涵!*/
可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本、同时充分利用它们之间的共同数据来减少时间和空间消耗。/*找不到比较科学的定义,就拿这个凑凑数吧~~~*/
这个数据结构很坑啊,我研究了一整天才差不多理解了一些(。。太笨了。。。)。所以,要理解好每一个域或变量的意义。
开讲!
一些数据结构,比如线段树或平衡树,他们一般是要么维护每个元素在原序列中的排列顺序,要么是维护每个元素的大小顺序,若是像二者兼得。。(反正我是觉得很。。)那么,这道题就想想主席树吧~/*还可以用划分树做*/
开讲!~好像说过一边了
既然叫“函数式线段树”,那么就应该有跟普通线段树相同的地方。一颗线段树,只能维护一段区间里的元素。但是,每个询问的区间都不一样,若是对每段区间都单独建立的线段树,那~萎定了~。因此,就要想,如何在少建,或建得快的情况下,能利用一些方法,得出某个区间里的情况。
比如一棵线段树,记为tree[i][j],表示区间[i,j]的线段树。那么,要得到它的情况,可以利用另外两棵树,tree[1][i-1]和tree[1][j],得出来。也就是说,可以由建树的一系列历史版本推出。
那么,怎么创建这些树呢?
首先,离散化数据。因为如果数据太大的话,线段树会爆~~
在所有树中,是按照当前区间元素的离散值(也就是用大小排序)储存的,在每个节点,存的是这个区间每个元素出现的次数之和(data域)。出现的次数,也就是存了多少数进来(建树时,是一个数一个数地存进来的)。
先建议棵线段树,所有的节点data域为0。再一个节点一个节点地添加。把每个数按照自己的离散值,放到树中合适的位置,然后data域+1,回溯的时候也要+1。当然,不能放到那棵空树中,要重新建树。第i棵树存的是区间(原序列)[1,i]。但是,如果是这样,那么会MLE+TLE。因此,要充分利用历史版本。用两个指针,分指当前空树和前一棵树。因为每棵树的结构是一样的,只是里面的data域不同,但是两棵相邻的树,只有一数只差,因此,如果元素要进左子树的话,右子树就会跟上个树这个区间的右子树是完全一样的,因此,可以直接将本树本节点的右子树指针接到上棵树当前节点的右儿子,这样即省时间,又省空间。
每添加一个节点(也就是新建一棵树)的复杂度是O(logn),因此,这一步的复杂度是O(nlogn)。
建完之后,要怎么查找呢?
跟一般的,在整棵树中找第k个数是一样的。如果一个节点的左权值(左子树上点的数量之和)大于k,那么就到左子树查找,否则到右子树查找。其实主席树是一样的。对于任意两棵树(分别存区间[1,i]和区间[1,j] i<j),在同一节点上(两节点所表示的区间相同),data域之差表示的是,原序列区间[i,j]在当前节点所表示的区间里,出现多少次(有多少数的大小是在这个区间里的)。同理,对于同一节点,如果在两棵树中,它们的左权值之差大于等于k,那么要求的数就在左孩子,否则在右孩子。当定位到叶子节点时,就可以输出了。
——————————————————————————————————————————————————————————————————————————————————————————————
鄙人的一些理解:所谓主席树呢,就是对原来的数列[1..n]的每一个前缀[1..i](1≤i≤n)建立一棵线段树,线段树的每一个节点存某个前缀[1..i]中属于区间[L..R]的数一共有多少个(比如根节点是[1..n],一共i个数,sum[root] = i;根节点的左儿子是[1..(L+R)/2],若不大于(L+R)/2的数有x个,那么sum[root.left] = x)。若要查找[i..j]中第k大数时,设某结点x,那么x.sum[j] - x.sum[i - 1]就是[i..j]中在结点x内的数字总数。而对每一个前缀都建一棵树,会MLE,观察到每个[1..i]和[1..i-1]只有一条路是不一样的,那么其他的结点只要用回前一棵树的结点即可,时空复杂度为O(nlogn)。
代码(最原始的树所有结点的值都为0,就算建好一棵树了……):
#include <cstdio>
#include <algorithm>
using namespace std; const int MAXN = ; struct Node {
int L, R, sum;
};
Node T[MAXN * ];
int T_cnt; void insert(int &num, int &x, int L, int R) {
T[T_cnt++] = T[x]; x = T_cnt - ;
++T[x].sum;
if(L == R) return ;
int mid = (L + R) >> ;
if(num <= mid) insert(num, T[x].L, L, mid);
else insert(num, T[x].R, mid + , R);
} int query(int i, int j, int k, int L, int R) {
if(L == R) return L;
int t = T[T[j].L].sum - T[T[i].L].sum;
int mid = (R + L) >> ;
if(k <= t) return query(T[i].L, T[j].L, k, L, mid);
else return query(T[i].R, T[j].R, k - t, mid + , R);
} struct A {
int x, idx;
bool operator < (const A &rhs) const {
return x < rhs.x;
}
}; A a[MAXN];
int rank[MAXN], root[MAXN];
int n, m; int main() {
T[].L = T[].R = T[].sum = ;
root[] = ;
while(scanf("%d%d", &n, &m) != EOF) {
for(int i = ; i <= n; ++i) {
scanf("%d", &a[i].x);
a[i].idx = i;
}
sort(a + , a + n + );
for(int i = ; i <= n; ++i) rank[a[i].idx] = i;
T_cnt = ;
for(int i = ; i <= n; ++i) {
root[i] = root[i - ];
insert(rank[i], root[i], , n);
}
while(m--) {
int i, j, k;
scanf("%d%d%d", &i, &j, &k);
printf("%d\n", a[query(root[i - ], root[j], k, , n)].x);
}
}
}
POJ 2104 K-th Number(主席树——附讲解)的更多相关文章
- 【POJ 2104】 K-th Number 主席树模板题
达神主席树讲解传送门:http://blog.csdn.net/dad3zz/article/details/50638026 2016-02-23:真的是模板题诶,主席树模板水过.今天新校网不好,没 ...
- poj2104 k-th number 主席树入门讲解
poj2104 k-th number 主席树入门讲解 定义:主席树是一种可持久化的线段树 又叫函数式线段树 刚开始学是不是觉得很蒙逼啊 其实我也是 主席树说简单了 就是 保留你每一步操作完成之后 ...
- 静态区间第k大(主席树)
POJ 2104为例(主席树入门题) 思想: 可持久化线段树,也叫作函数式线段树,也叫主席树(高大上). 可持久化数据结构(Persistent data structure):利用函数式编程的思想使 ...
- poj 2104 K-th Number 主席树+超级详细解释
poj 2104 K-th Number 主席树+超级详细解释 传送门:K-th Number 题目大意:给出一段数列,让你求[L,R]区间内第几大的数字! 在这里先介绍一下主席树! 如果想了解什么是 ...
- POJ 2104 K-th Number 主席树(区间第k大)
题目链接: http://poj.org/problem?id=2104 K-th Number Time Limit: 20000MSMemory Limit: 65536K 问题描述 You ar ...
- POJ 2104:K-th Number(主席树静态区间k大)
题目大意:对于一个序列,每次询问区间[l,r]的第k大树. 分析: 主席树模板题 program kthtree; type point=record l,r,s:longint; end; var ...
- POJ 2104 K-th Number ( 求取区间 K 大值 || 主席树 || 离线线段树)
题意 : 给出一个含有 N 个数的序列,然后有 M 次问询,每次问询包含 ( L, R, K ) 要求你给出 L 到 R 这个区间的第 K 大是几 分析 : 求取区间 K 大值是个经典的问题,可以使用 ...
- SPOJ MKTHNUM & POJ 2104 - K-th Number - [主席树模板题]
题目链接:http://poj.org/problem?id=2104 Description You are working for Macrohard company in data struct ...
- poj 2104 K-th Number(主席树 视频)
K-th Number 题意: 给你一些数,让你求一个区间内,第k大的数是多少. 题解: 主席树第一题,看的qsc视频写的,戳戳戳 学到了unique函数,他的作用是:把相邻的重复的放到后面,返回值是 ...
随机推荐
- 51nod算法马拉松14
这次太丢人了只搞出来了A到D,那就将就写一写A到D... A 棋盘问题 脑筋急转弯题,不难发现每一次两个人只能染白奇数个格子,所以数数有奇数还是偶数个白格子就行了. #include<cstdi ...
- ZeroMQ实例-使用ZeroMQ进行windows与linux之间的通信
1.本文包括 1)在windows下使用ZMQ 2)在windows环境下与Linux环境下进行网络通信 2.在Linux下使用ZMQ 之前写过一篇如何在Linux环境下使用ZMQ的文章 <Ze ...
- error while loading shared libraries: libXXX.so.x: cannot open shared object file: No such file or directory .
转载:http://www.eefocus.com/pengwr/blog/2012-02/235057_baf52.html 此时你可以locate libXXX.so.x 一下,查看系统里是否有该 ...
- IOS 学习资料
IOS学习资料 - 逆天整理 - 精华无密版[最新][精华] 无限互联3G学院 iOS开发视频教程UI 极客学院IOS iPhone 6的自适应布局
- [LintCode] Backpack VI 背包之六
Given an integer array nums with all positive numbers and no duplicates, find the number of possible ...
- android获取系统通讯录
package com.example.administrator.yunphone.View; import android.app.Fragment; import android.databas ...
- js实现事件模型bind与trigger
function Emitter() { this._listener = [];//_listener[自定义的事件名] = [所用执行的匿名函数1, 所用执行的匿名函数2] } //注册事件 Em ...
- shodan:黑客搜索引擎
Shodan是msfconsole创始人与几个爱好技术的黑客开发的一款黑客搜索引擎,被称为可怕的搜索引擎,详细介绍见百度百科 网站地址:www.shodan.io 免费注册一账号,无账号的话,默认只显 ...
- Web前端代码页面布局总结
一. html (1)编码:所有编码均采用xhtml,标签必须闭合,属性值用双引号包括,编码统一为utf-8. (2)语义化:语义化html,正确使用标签. (3)文件命名:命名以中文命名,依实际模 ...
- ASM文件系统
1.确认数据库版本 2.个人理解的存储解决方案的发展趋势 2.1图示说明 2.2图示描述 如上图我们描述了在不同时期的IT行业(数据库)出现的存储文件系统,下面我们将分别说明: ü 裸设备:所谓裸设 ...