poj 2104 划分树(查询区间第k大数)
来源:互联网 发布:搭建高并发java框架 编辑:程序博客网 时间:2024/06/06 01:42
题意:给定一个n个数的数组,有m个查询,每个查询为[a,b,k],意思是区间[a,b]的第k大数。
思路:由这个题认识了什么叫做划分树。划分树的基本思想就是对于某个区间,把它划分成两个子区间,左边区间的数小于右边区间的数。查找的时候通过记录进入左子树的数的个数,确定下一个查找区间,最后范围缩小到1,就找到了。
建树的过程比较简单,对于区间[l,r],首先通过对原数组的排序找到这个区间的中位数a[mid],小于a[mid]的数划入他的左子树[l,mid-1],大于它的划入右子树[mid,r]。同时,对于第i个数,记录在[l,i]区间内有多少数被划入左子树。最后,对它的左子树区间[l,mid-1]和右子树区间[mid,r]递归的继续建树就可以了。
建树的时候要注意对于被分到同一子树的元素,元素间的相对位置不能改变。
查找的过程:查找深度为h,在大区间[L,R]中找小区间[left,right]中的第k元素。再看看他是如何工作的。我们的想法是,先判断[left,right]中第k元素在[L,R]的哪个子树中,然后找出对应的小区间和k,递归的进行查找,直到小区间的left = right为止。具体思路见代码中注释。
注意:num数组除了这种建立方法还可以在建树时以每个区间为单位建立(百度百科的方法),
if( i==l )num[c][i]=0;
else
num[c][i]=num[c][i-1];
这样建树在查询的时候代码能少写一些。
#include <cstdio>#include <algorithm>#define N 100005using namespace std;int s[N],t[25][N],num[25][N];int n,m;void build(int left,int right,int d){ int i,j,k,mid,ml,flag; if(left == right) return; mid = (left+right)>>1; ml = mid-left+1; for(i = left;i<=right;i++)//注意是从left到right而不是到mid if(t[d][i] < s[mid]) ml--; j = left,k = mid+1; for(i = left;i<=right;i++){ flag = 0; if(t[d][i] < s[mid] || (t[d][i]==s[mid]&&ml)){//表示进入左子树 flag = 1; if(t[d][i] == s[mid])//如果是和中位数相等的情况 ml--; t[d+1][j++] = t[d][i]; }else t[d+1][k++] = t[d][i]; num[d][i] = num[d][i-1]+flag;//记录进入左子树的个数 } build(left, mid, d+1); build(mid+1, right, d+1);}int query(int left,int right,int k,int L,int R,int d){ int mid,x,y,rx,ry,val; if(left == right) return t[d][left]; mid = (L+R)>>1; x = num[d][left-1] - num[d][L-1];//[L,left)内去往左子树的个数 y = num[d][right] - num[d][L-1];//[L,right]内去往左子树的个数 val = y-x;//[left,right]内去往左子树的个数 rx = left - L - x;//[L,left)内去往右子树的个数 ry = right - L + 1 - y;//[L,right]内去往右子树的个数 if(k <= val) return query(L+x, L+y-1, k, L, mid, d+1); return query(mid+1+rx, mid+ry, k-val, mid+1, R, d+1);}int main(){ int i,j,k,x; scanf("%d %d",&n,&m); for(i = 1;i<=n;i++){ scanf("%d",&s[i]); t[0][i] = s[i]; } sort(s+1,s+1+n); build(1,n,0); for(i = 0;i<m;i++){ scanf("%d %d %d",&j,&k,&x); printf("%d\n",query(j,k,x,1,n,0)); }}/*t数组值如下,四周序号表示下标t 1 2 3 4 5 6 70 1 5 2 6 3 7 41 1 2 3 4 5 6 72 1 2 3 4 5 6 73 1 2 3 4 5 6 0 num数组值如下,四周序号表示下标num 1 2 3 4 5 6 7 0 1 1 2 2 3 3 4 1 1 2 2 2 3 4 4 2 1 1 2 2 3 3 0*/
0 0
- poj 2104 划分树(查询区间第k大数)
- POJ 2104 K-th Number(区间第k大数)(平方分割,归并树,划分树)
- poj查询区间第k大(划分树)
- 划分树学习小记 Poj 2104+Poj 2761+Hdu 2665 (区间第k大数)
- POJ 2104 区间第K大值(划分树做法)
- 小白算法练习 POJ 2104 K-th 区间K大数 划分树
- 区间第K大数——划分树(POJ2104解题报告)
- POJ 2104-K-th Number(划分树)求区间内第k小的数
- 【划分树】求区间K大数
- POJ2104-K-th Number-求区间第K大数(暴力or归并树or划分树)
- POJ2104 K-th Number 划分树求区间第K大数
- 划分树的用法(一):查询区间第K大值值(poj2104)
- 划分树的用法(一):查询区间第K大值值(poj2104)
- POJ 2761-Feed the dogs(划分树)求区间内第k小的数
- poj 2104 查询区间第k小 主席树 (递归和非递归)模板
- 区间K大数查询
- 区间K大数查询
- 区间k大数查询
- 数据库一般操作
- Python标准库 datetime
- javascript高级特性
- 比ThreadPool对象更好用的线程控制对象
- hibernate多表查询 join
- poj 2104 划分树(查询区间第k大数)
- C++动态申请二维数组
- org.dom4j.DocumentException: org.dom4j.DocumentFactory cannot be cast to org.dom4j.DocumentFactory
- ubuntu 操作
- Python标准库time
- Android原理——SavedState
- VC中如何让一个应用程序只启动一次
- 统计学习方法
- iOS系统网络抓包方法