http://poj.org/problem?id=2104
http://acm.hdu.edu.cn/showproblem.php?pid=2665
题意:
给定一个长度为n的序列,求一个区间[L,R]内第K大的数;
思路:
划分树模板题。
给出个人感觉讲解比较好的连接:
http://blog.sina.com.cn/s/blog_5f5353cc0100ki2e.html
http://www.notonlysuccess.com/index.php/divide-tree/
pku poj2761一样的题目:
个人理解:
划分树就是按层划分区间,以排好序的mid为基准进行划分。建好树之后关键在于询问,我们试图将询问区间[L,R]不断缩小,注意这里递归的时候可能不再严格的遵守L,R而是新的包含L,R内的数的新的Li,Ri.这里理解了很长时间。。。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a , b) ((a) < (b) ? (a) : (b)) #define Max(a , b) ((a) > (b) ? (a) : (b)) #define ll __int64 #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 100007 #define N 100007 using namespace std; //freopen("din.txt","r",stdin); struct node{ int l,r; int mid(){ return (l + r)>>1; } }tt[N<<2]; int toLeft[20][N]; int val[20][N],sorted[N]; int n,q; void build(int l,int r,int rt,int d){ int i; tt[rt].l = l; tt[rt].r = r; if (l == r) return ; int m = tt[rt].mid(); int lsame = m - l + 1;//先假设做区间与m相等的放满 //一一排除,保证左区间放的是<=sorted[m]的值 for (i = l; i <= r; ++i){ if (val[d][i] < sorted[m]) lsame--; } int lpos = l; int rpos = m + 1; int same = 0; for (i = l; i <= r; ++i){ if (i == l) toLeft[d][i] = 0;//toLeft[i]表示[ tt[rt].l , i ]区域里有多少个数分到左边 else toLeft[d][i] = toLeft[d][i - 1]; if (val[d][i] < sorted[m]){ toLeft[d][i]++; val[d + 1][lpos++] = val[d][i]; } else if (val[d][i] > sorted[m]){ val[d + 1][rpos++] = val[d][i]; } else{ if (same < lsame){//有lsame的数是分到左边的 toLeft[d][i]++; val[d + 1][lpos++] = val[d][i]; same++; } else{ val[d + 1][rpos++] = val[d][i]; } } } build(lc,d + 1); build(rc,d + 1); } int query(int L,int R,int k,int d,int rt){ // printf("%d %d\n",L,R); if (L == R){ return val[d][L]; } int s = 0;;//s表示[ L , R ]有多少个分到左边 int ss = 0;//ss表示 [tt[rt].l , L-1 ]有多少个分到左边 if (L == tt[rt].l){ ss = 0; s = toLeft[d][R]; } else{ ss = toLeft[d][L - 1]; s = toLeft[d][R] - toLeft[d][L - 1]; } if (k <= s){//有多于k个分到左边,显然去左儿子区间找第k个 int newl = tt[rt].l + ss; int newr = newl + s - 1; return query(newl,newr,k,d + 1,rt<<1); } else{ int m = tt[rt].mid(); int bb = L - tt[rt].l - ss;//bb表示 [tt[rt].l , L-1 ]有多少个分到右边 int b = R - L + 1 - s;//b表示 [L , R]有多少个分到右边 int newl = m + bb + 1; int newr = newl + b - 1; return query(newl,newr,k - s,d + 1,rt<<1|1); } } int main(){ //freopen("din.txt","r",stdin); int i; int x,y,k; scanf("%d%d",&n,&q); for (i = 1; i <= n; ++i){ scanf("%d",&val[0][i]); sorted[i] = val[0][i]; } sort(sorted + 1,sorted + 1 + n); build(1,n,1,0); while (q--){ scanf("%d%d%d",&x,&y,&k); printf("%d\n",query(x,y,k,0,1)); } return 0; }