poj 2104 k-th number
来源:互联网 发布:海康威视算法工程师 编辑:程序博客网 时间:2024/06/16 03:30
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
Sample Input
7 31 5 2 6 3 7 42 5 34 4 11 7 3
Sample Output
563
Hint
Source
题解:这是一道主席树静态区间查询第K大的模板题
研究了一下午的主席树,总算懂得了一点眉目。
什么是主席树
可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本、同时充分利用它
们之间的共同数据来减少时间和空间消耗。
因此可持久化线段树也叫函数式线段树又叫主席树。
主席树的主体是线段树,跟准确的说是由多颗形态相同的线段树所构成的。所谓主席树就是给每个区间[1,i]都建立一颗线段树,但如果每个区间都重新构建线段树的话不仅在时间是无法承受,空间也大的惊人。于是就发现了主席树一个很好的性质,那就是每新建的一个区间都是依附前一个区间存在的,什么意思呢?就是要充分他的前一颗树。用两个指针,分指当前空树和前一棵树。因为每棵树的结构是一样的,只是某些节点控制的数目不同,但是两棵相邻的树,只有一数只差,因此,如果元素要进左子树的话,右子树就会跟上个树这个区间的右子树是完全一样的,因此,
可以直接将本树本节点的右子树指针接到上棵树当前节点的右儿子,并且对于每次更改,最多增加logn 个节点,所以空间复杂度约为nlogn ,这样即省时间,又省空间。
具体情况见下图:
在建立线段树时一般都需要记录三个值,左右子树根节点的编号以及这颗线段树中一共有多少个数。
在建树时必须要保证这颗线段树的先序遍历是升序排列的,也就是说当前这个数在原数列中是第几大,那么他在线段
树[1,n]的区间中的位置就在哪,即为下标。那有人肯定会问,如果数值很大的话,该怎么办呢?这里引入一种思想
——离散化。
何为离散化?
就是给一个数列按从小到大的顺序编号,赋予他一个新的值。
举个例子:
原数列 12 15 87 69 42
离散化后 1 2 5 4 3
有了这些铺垫,就可以用主席树求解区间第K小了
因为每棵树的形态相同,那么就可以做减法。跟一般的,在整棵树中找第k个数是一样的。如果一个节点的左权值(左
子树上点的数量之和)大于k,那么就到左子树查找,否则到右子树查找。其实主席树也一样的。对于任意两棵树(分
别存区间[1,i]和区间[1,j] i<j),在同一节点上(两节点所表示的区间相同),控制节点的个数之差表示的是,原序列
区间[i,j]在当前节点所表示的区间里,有多少数是在这个区间里的。同理,对于同一节点,如果在两棵树中,它们的点
数之差大于等于k,那么要求的数就在左孩子,否则在右孩子。当递归到叶子节点时,就可以输出了。具体的过程可以
结合代码,感性的思考一下(其实有点类似于平衡树找第K大的感觉)。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define N 100003 #define M 400003 using namespace std; int a[N],b[N],p[N],root[N],n,m,sz; struct data { int l,r,w; };data tree[M*20]; int cmp(int x,int y) { return a[x]<a[y]; } void insert(int &i,int l,int r,int x) { tree[++sz]=tree[i]; i=sz; tree[i].w++; if (l==r) return; int mid=(l+r)>>1; if (x<=mid) insert(tree[i].l,l,mid,x); else insert(tree[i].r,mid+1,r,x); } int query(int i,int j,int l,int r,int k) { if (l==r) return l; int t=tree[tree[j].l].w-tree[tree[i].l].w; int mid=(l+r)>>1; if (t>=k) return query(tree[i].l,tree[j].l,l,mid,k); else return query(tree[i].r,tree[j].r,mid+1,r,k-t); } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) { scanf("%d",&a[i]); p[i]=i; } sort(p+1,p+n+1,cmp); for (int i=1;i<=n;i++) b[p[i]]=i;//离散化,p[i]表示第i小的值在a[]中的下标 sz=0; root[0]=0; for (int i=1;i<=n;i++) { root[i]=root[i-1]; insert(root[i],1,n,b[i]); } for (int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); int t=query(root[x-1],root[y],1,n,z); printf("%d\n",a[p[t]]); } }
- poj 2104 K-th Number
- poj 2104 K-th Number
- Poj 2104 K-th Number
- POJ-2104-K-th Number
- POJ 2104 K-th Number
- POJ-2104-K-th Number
- POJ 2104 K-th Number
- POJ 2104 K-th Number
- POJ 2104 K-th Number
- POJ 2104 K-th Number
- POJ 2104 K-th Number
- poj-2104 K-th Number
- POJ 2104 K-th Number
- 【POJ 2104】K-th Number
- POJ 2104 K-th Number
- POJ 2104 K-th Number
- POJ 2104 K-th Number
- poj 2104 K-th Number
- IPC机制之使用ContentProvider
- thinkphp自定义模板标签(一)
- SGI STL的空间配置器alloc
- 浅析设计模式之观察者模式
- (java) Populating Next Right Pointers in Each Node
- poj 2104 k-th number
- C语言模仿C#事件
- BigPipe——流水线式的动态并行处理
- 继承中的四个“不能”
- Android IPC之AIDL浅谈
- OpenStack存储相关组件描述
- 关于UITableView 的一些重用技术注意点
- FZU 2191完美的数字 (暴力)
- Swift泛型