POJ2104:划分树求区间第k小
2017-08-29 00:28
260 查看
题意:给出n个数字(n<=1e5),现在有m次询问(m<=5000),每次询问(l,r,k),求出原数列的[ l,r ]区间内的第k小的数字。
题解:POJ数据水是出了名的。这个题甚至可以对每次询问把元素拷贝出来排个序搞定。。但是我拿这个题搞了搞划分树模板。
划分树是一个二叉树,每个节点保存一段区间。build每次把一个区间[ L , R ]划分成两个区间[ L, Mid ]和[ Mid+1 , R ](让一些数字进入左子区间,剩下的进入右子区间)使得 左子区间的每个数字 都不大于 右子区间的每个数字。同时数字原来的相对顺序保持不变,这就构成了val域,同时辅助开一个num域,记录我维护的这一段区间数字,i位置以及之前总共有多少个将会进入我的左孩子。我们考虑每一层的所有节点他们保存的数字,顺次连接就是得到了长度为n的数组,只是数字被划分过,顺序不同,于是我们可以开val[
20 ][ N ]这样的数组来记录树的节点。(2^20=1e6),相应有num[ 20 ][ N ]。查询的时候,其实维护两个东西:答案在左边还是右边,如果答案在右孩子,计算查询区间在右孩子的有效范围,左边同理。
Code:
题解:POJ数据水是出了名的。这个题甚至可以对每次询问把元素拷贝出来排个序搞定。。但是我拿这个题搞了搞划分树模板。
划分树是一个二叉树,每个节点保存一段区间。build每次把一个区间[ L , R ]划分成两个区间[ L, Mid ]和[ Mid+1 , R ](让一些数字进入左子区间,剩下的进入右子区间)使得 左子区间的每个数字 都不大于 右子区间的每个数字。同时数字原来的相对顺序保持不变,这就构成了val域,同时辅助开一个num域,记录我维护的这一段区间数字,i位置以及之前总共有多少个将会进入我的左孩子。我们考虑每一层的所有节点他们保存的数字,顺次连接就是得到了长度为n的数组,只是数字被划分过,顺序不同,于是我们可以开val[
20 ][ N ]这样的数组来记录树的节点。(2^20=1e6),相应有num[ 20 ][ N ]。查询的时候,其实维护两个东西:答案在左边还是右边,如果答案在右孩子,计算查询区间在右孩子的有效范围,左边同理。
Code:
#include<stdio.h> #include<algorithm> using namespace std; const int MAX = 1e5+10; int val[20][MAX]; int num[20][MAX]; int sorted[MAX]; int n,m; void build (int l,int r,int deep){ if (l==r){ return; } int mid = l+r >>1; int issame = mid-l+1; int MV = sorted[mid]; for (int i=l;i<=r;i++){ if (val[deep][i]<MV){ issame--; } } int ln =l,rn = mid+1; for (int i=l;i<=r;i++){ if (i==l){ num[deep][i] =0; }else{ num[deep][i] = num[deep][i-1]; } if (val[deep][i]<MV||val[deep][i]==MV&&issame){ val[deep+1][ln++]=val[deep][i]; num[deep][i]++; if (val[deep][i]==MV){ issame--; } }else{ val[deep+1][rn++] = val[deep][i]; } } build(l,mid,deep+1); build(mid+1,r,deep+1); } int look(int deep,int l,int r,int L,int R,int K){ if (l==r){ return val[deep][l]; } int lcnt; if (l==L){ lcnt =0; }else{ lcnt = num[deep][L-1]; } int totl = num[deep][R]-lcnt; if (totl>=K){ return look(deep+1,l,l+r>>1,l+lcnt,l+lcnt+totl-1,K); }else{ int ry = (l+r)/2+1+L-l-lcnt; return look(deep+1,(l+r)/2+1,r,ry,ry+R-L+1-totl-1,K-totl); } } void work(){ for (int i=1;i<=n;i++){ scanf("%d",&val[0][i]); sorted[i] = val[0][i]; } sort(sorted+1,sorted+n+1); build(1,n,0); while (m--){ int l,r,k; scanf("%d%d%d",&l,&r,&k); printf("%d\n",look(0,1,n,l,r,k)); } } int main(){ while (scanf("%d%d",&n,&m)!=EOF){ work(); } return 0; }
相关文章推荐
- 划分树的用法(一):查询区间第K大值值(poj2104)
- poj2104 求区间第k大数(划分&&主席--待补)
- 划分树的用法(一):查询区间第K大值值(poj2104)
- 划分树的用法(一):查询区间第K大值值(poj2104)
- 查询区间第k大 POJ2104 暴力 or 划分树 or 归并树
- POJ2104 POJ2761 区间第K大 主席树
- 划分树——求区间第k大值
- 划分树学习小记 Poj 2104+Poj 2761+Hdu 2665 (区间第k大数)
- 可持久化线段树(主席树)(图文并茂详解)【poj2104】【区间第k大】
- POJ 2104 区间第K大值(划分树做法)
- 划分树的学习(求区间第k大的数字)
- POJ 2104 区间第K大值(划分树做法)
- [NBUT 1458 Teemo]区间第k大问题,划分树
- HDU 3473 Minimum Sum (划分树求区间第k大带求和)(转)
- 可持久化线段树(主席树)(图文并茂详解)【poj2104】【区间第k大】
- hdu 4417 Super Mario (二分法 + 划分树求区间第K大)
- poj2104 划分树 区间K大 在线 无修改
- 划分树(基本用法是求给定区间的第k大的值)
- POJ2104-K-th Number-求区间第K大数(暴力or归并树or划分树)
- 区间第k小 poj2104 可持久化线段树