hdu 2655 (求区间K小数)
2013-08-18 20:35
155 查看
题目
划分树吧,在这学得
记没排序的数组为UA(unsorted array,即原数组),排序数组SA(sorted array)
划分树的建树过程:将区间为[L,R]的UA,按SA[MID]来划分,如果比SA[MID]值小,则分到左子树,比其大,则分到右子树,并把在某个位置小于等于SA[MID]的个数记录下来,直到区间长度为1为止。
Query函数在做的一个工作将区间长度逐渐缩为1。以下以在[L,R]内找第CNT大的数为例说明。假设
S表示在[L,R]内有几个小于等于DATA[MID]的个数
SS表示在[tr[root].l,l-1]内有几个小于等于DATA[MID]的个数
当S>=CNT时,将L变为tr[root].l+SS,因为现在的S已经比CNT大了,还要SS那部分有什么用,
而将R变为tr[root].l+ss+s-1则相当于将下一次的S值减少1,以使S趋向于CNT
示意图:
(注意:SS的值一定<=L-tr[root].l)
当S<CNT时,将L变为mid+(l-tr[root].l-ss)+1,括号内的数是为了进一步缩小区间长度用的。因为在[tr[root].l , l-1 ]内比DATA[MID]大的数,在下一步显然不需要
将R变为mid+l-tr[root].l-ss+(r-l+1-s),括号内的数是为了确定新区间的长度。即在[L,R]内有几个比DATA[MID]大的数。
划分树吧,在这学得
记没排序的数组为UA(unsorted array,即原数组),排序数组SA(sorted array)
划分树的建树过程:将区间为[L,R]的UA,按SA[MID]来划分,如果比SA[MID]值小,则分到左子树,比其大,则分到右子树,并把在某个位置小于等于SA[MID]的个数记录下来,直到区间长度为1为止。
Query函数在做的一个工作将区间长度逐渐缩为1。以下以在[L,R]内找第CNT大的数为例说明。假设
S表示在[L,R]内有几个小于等于DATA[MID]的个数
SS表示在[tr[root].l,l-1]内有几个小于等于DATA[MID]的个数
当S>=CNT时,将L变为tr[root].l+SS,因为现在的S已经比CNT大了,还要SS那部分有什么用,
而将R变为tr[root].l+ss+s-1则相当于将下一次的S值减少1,以使S趋向于CNT
示意图:
(注意:SS的值一定<=L-tr[root].l)
当S<CNT时,将L变为mid+(l-tr[root].l-ss)+1,括号内的数是为了进一步缩小区间长度用的。因为在[tr[root].l , l-1 ]内比DATA[MID]大的数,在下一步显然不需要
将R变为mid+l-tr[root].l-ss+(r-l+1-s),括号内的数是为了确定新区间的长度。即在[L,R]内有几个比DATA[MID]大的数。
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define N 100010 int sa ,tree[20] ,sum[20] ; //sum[i][j]表示i层,1~j里比sa[m]小的数 int n,m,t,x,y,k; struct node { int l,r; }root[N*4]; inline void build(int t,int d,int x,int y) { root[t].l=x; root[t].r=y; if(x==y) return; int m=(x+y)>>1; int lless=m-x+1; for(int i=x;i<=y;i++) { if(tree[d][i]<sa[m]) lless--; } int lp=x,rp=m+1; for(int i=x;i<=y;i++) { if(i==x) sum[d][i]=0; else sum[d][i]=sum[d][i-1]; if(tree[d][i]<sa[m]) { sum[d][i]++; tree[d+1][lp++]=tree[d][i]; } else if(tree[d][i]>sa[m]) { tree[d+1][rp++]=tree[d][i]; } else { if(lless>0) { sum[d][i]++; lless--; tree[d+1][lp++]=tree[d][i]; } else { tree[d+1][rp++]=tree[d][i]; } } } build(t*2,d+1,x,m); build(t*2+1,d+1,m+1,y); } inline int query(int t,int d,int x,int y,int k) { if(x==y) return tree[d][x]; int ss,s; int l=root[t].l,r=root[t].r,m=(l+r)>>1; //ss:[l,x)之间被分到左子树的,s:[x,y]被分到左子树的 if(l==x) { ss=0; s=sum[d][y]; } else { ss=sum[d][x-1]; s=sum[d][y]-sum[d][x-1]; } if(s>=k) { int xx=l+ss;//既然[x,y]的就够了,[l,x-1]就不用了里的ss个就没用了。 int yy=l+ss+s-1;//将下一次的s减1 return query(t*2,d+1,xx,yy,k); } else { int r1=x-l-ss;//[l,x-1]之间被分到右子树 int r2=y-x+1-s;//[x,y]之间被分到右子树 int xx=m+1 +r1; int yy=m+1 +r1+r2-1; return query(t*2+1,d+1,xx,yy,k-s); } } int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++) { scanf("%d",&sa[i]); tree[0][i]=sa[i]; } sort(sa+1,sa+n+1); build(1,0,1,n); for(int i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&k); printf("%d\n",query(1,0,x,y,k)); } } return 0; }
相关文章推荐
- HDU 4745 Two Rabbits 区间DP
- hdu 4893(线段树 成段更新+区间合并)
- HDU 4283 You Are the One(区间DP)
- HDU 5692 Snacks( dfs序 + 线段树处理区间求和)
- 线段树,区间更改,延迟标记 hdu 1698
- HDU 1166 敌兵布阵-线段树-(单点更新,区间查询)
- HDU 3974 Assign the task 线段树(树映射到区间)
- 【HDU 2871 Memory Control】线段树之区间操作
- hdu 4288 Coder 线段树 区间合并
- HDU-4283(区间DP)
- 求区间内和问题(线段树)(hdu 1166)
- HDU 2476 String painter(区间DP)
- HDU 1540 Tunnel Warfare (线段树区间合并)
- HDU 3397 Sequence operation(区间合并)
- HDU - 3974 Assign the task (dfs序+区间更新 单点查询)
- hdu 3974 Assign the task(区间建树)(区间更新+单点查询)
- HDU1754 I Hate It 线段树 区间更新 区间查找 最大值
- hdu 2871 线段树+区间合并 好题
- hdu 4970 树状数组区间更新 思维题
- http://acm.hdu.edu.cn/showproblem.php?pid=1166&&线段树区间求和