题目:http://poj.org/problem?id=2104
划分树待我好好理解下再写个教程吧,觉得网上的内容一般,,,
模板题:
贴代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define CLR(a) memset(a,0,sizeof(a)) const int MAXN = 100010; struct Node { int l,r; int len(){return r-l;} int mid(){return (l+r)/2;} bool in(int ll,int rr){return l>=ll && r<=rr;} void lr(int ll,int rr){l = ll; r=rr;} }node[MAXN*4]; int num_left[20][MAXN],seg[20][MAXN],sa[MAXN]; //sa数组存sort后的结果 //seg数组存的是d层划分后的数字 (类似快排Partation (d-1)次后的结果) //num_left存的是d层在i之前(包括i)小于 sa[mid] 的数的数目 void Init() { //CLR(seg),CLR(num_left),CLR(node); memset(seg,0,sizeof(seg)); memset(num_left,0,sizeof(num_left)); memset(node,0,sizeof(node)); } void PaBuild(int t,int l, int r,int d) { node[t].lr(l,r); if(node[t].len() == 0)return; int mid=(l+r)/2,lsame=mid-l+1; for(int i=l;i<=r;i++)//首先确定分到每一侧的数的数目 if(seg[d][i] < sa[mid])//因为相同的元素可能被分到两侧 lsame--; int lpos=l,rpos=mid+1; for(int i=l;i<=r;i++) { if(i == l) num_left[d][i]=0; else num_left[d][i]=num_left[d][i-1]; if(seg[d][i] < sa[mid]) { num_left[d][i]++; seg[d+1][lpos++]=seg[d][i]; } if(seg[d][i] > sa[mid]) seg[d+1][rpos++] = seg[d][i]; if(seg[d][i] == sa[mid]) if(lsame > 0)// 如果大于0,说明左侧可以分和sa[mid]相同的数字 { lsame--; num_left[d][i]++; seg[d+1][lpos++]=seg[d][i]; } else //反之,说明左侧数字已经分满了,就分到右边去 seg[d+1][rpos++]=seg[d][i]; } PaBuild(t*2,l,mid,d+1); PaBuild(t*2+1,mid+1,r,d+1); } void Build(int s,int t) { sort(sa+s,sa+t+s); PaBuild(1,s,t,1); } int find_rank(int t,int l,int r,int d,int val)//val指的是k { if(node[t].len() == 0)return seg[d][l]; int s,ss; //s表示区间[l,r]有多少个小于sa[mid]的数被分到左边 if( l == node[t].l)//ss表示从当前区间的L到l-1有多少个小于sa[mid]的数被分到左边,L,R指的是树上当前节点的区间范围 ss=0; else ss=num_left[d][l-1]; s=num_left[d][r]-ss; if(s>=val) return find_rank(t*2, node[t].l+ss,node[t].l+ss+s-1,d+1,val); else { int mid = node[t].mid(); int bb=l-node[t].l-ss; //表示从当前区间L到l-1有多少个分到右边 int b=r-l+1-s; //表示[l,r]有多少个分到右边 return find_rank(t*2+1,mid+bb+1,mid+bb+b,d+1,val-s); } } int Query(int s,int t,int k) { return find_rank(1,s,t,1,k); } int main() { int n,m,x,y,k; while(~scanf("%d%d",&n,&m)) { Init(); for(int i=1;i<=n;i++) { scanf("%d",&sa[i]); seg[1][i] = sa[i]; } Build(1,n); while(m--) { scanf("%d%d%d",&x,&y,&k); int ans=Query(x,y,k); printf("%d\n",ans); } } return 0; }
poj 2401 划分树 求区间第k大的数,布布扣,bubuko.com
时间: 2024-12-26 16:11:03