看了一晚上hh大牛的划分树,总算看懂了是怎么回事了。。
hh大牛的博客,请戳这里
划分树是快排+线段树
先来一模版 POJ-2104-K-th Number
题意:给出n,m,n代表N个数,m代表查询的次数,再输入m行l,r,k;求在[l,r]区间中第k小的数;
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define MAXN 100050 #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 using namespace std; int tt[MAXN<<2];//呃,因为不要统计区间的操作,所以这个线段树一般有的数组,现在没有用了。。 int sorted[MAXN]; int toLeft[20][MAXN];//20代表的是20层 int val[20][MAXN]; void build(int d,int l,int r,int rt){ if(l==r)return; int mid=(l+r)>>1; int lsame=mid-l+1;//lsame表示和val_mid相等且分到左边的 for(int i=l;i<=r;i++){ if(val[d][i]<sorted[mid]){ lsame--;//先假设左边的数(mid - l + 1)个都等于val_mid,然后把实际上小于val_mid的减去 } } int lpos=l; int rpos=mid+1; int same=0; for(int i=l;i<=r;i++){ if(i==l){ toLeft[d][i]=0;//toLeft[i]表示[ l , i ]区域里有多少个数分到左边 } else{ toLeft[d][i]=toLeft[d][i-1]; } if(val[d][i]<sorted[mid]){ toLeft[d][i]++; val[d+1][lpos++]=val[d][i]; } else if(val[d][i]>sorted[mid]){ val[d+1][rpos++]=val[d][i]; } else{ if(same<lsame){//有lsame的数是分到左边的 same++; toLeft[d][i]++; val[d+1][lpos++]=val[d][i]; } else{ val[d+1][rpos++]=val[d][i]; } } } build(d+1,lson); build(d+1,rson); } int query(int L,int R,int k,int d,int l,int r,int rt){ if(l==r){ return val[d][l]; } int s;//s表示[ L , R ]有多少个分到左边 int ss;//ss表示 [l , L-1 ]有多少个分到左边 int mid=(l+r)>>1; if(L==l){ s=toLeft[d][R]; ss=0; } else{ s=toLeft[d][R]-toLeft[d][L-1]; ss=toLeft[d][L-1]; } if(s>=k){//有多于k个分到左边,显然去左儿子区间找第k个 int newl=l+ss; int newr=l+ss+s-1;//计算出新的映射区间 return query(newl,newr,k,d+1,lson); } else { int bb=L-l-ss;//bb表示 [l , L-1 ]有多少个分到右边 int b=R-L-s+1;//b表示 [L , R]有多少个分到右边 int newl=mid+bb+1; int newr=mid+bb+b; return query(newl,newr,k-s,d+1,rson); } } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&val[0][i]); sorted[i]=val[0][i]; } sort(sorted+1,sorted+n+1); build(0,1,n,1); while(m--){ int l,r,k; scanf("%d%d%d",&l,&r,&k); printf("%d\n",query(l,r,k,0,1,n,1)); } return 0; }