POJ 2104 划分树
划分树:查询区间第K大
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #define clc(a,b) sizeof(a,b,sizeof(a)) 6 using namespace std; 7 const int maxn=222; 8 const int MAXN = 100010; 9 int tree[20][MAXN]; 10 int sorted[MAXN]; 11 int toleft[20][MAXN]; 12 13 void build(int l,int r,int dep) { 14 if(l==r) 15 return; 16 int mid=(l+r)>>1; 17 int same= mid-l+1; 18 for(int i=l; i<=r; i++) { 19 if(tree[dep][i]<sorted[mid]) { 20 same--; 21 } 22 } 23 int lpos=l; 24 int rpos=mid+1; 25 for(int i=l; i<=r; i++) { 26 if(tree[dep][i]<sorted[mid]) 27 tree[dep+1][lpos++]=tree[dep][i]; 28 else if(tree[dep][i]==sorted[mid]&&same>0) { 29 tree[dep+1][lpos++]=tree[dep][i]; 30 same--; 31 } else 32 tree[dep+1][rpos++]=tree[dep][i]; 33 toleft[dep][i]=toleft[dep][l-1]+lpos-l; 34 } 35 build(l,mid,dep+1); 36 build(mid+1,r,dep+1); 37 } 38 39 int query(int L,int R,int l,int r,int dep,int k) { 40 if(l==r) return tree[dep][l]; 41 int mid=(L+R)>>1; 42 int cnt=toleft[dep][r]-toleft[dep][l-1]; 43 if(cnt>=k) { 44 int newl=L+toleft[dep][l-1]-toleft[dep][L-1]; 45 int newr=newl+cnt-1; 46 return query(L,mid,newl,newr,dep+1,k); 47 } else { 48 int newr=r+toleft[dep][R]-toleft[dep][r]; 49 int newl=newr-(r-l-cnt); 50 return query(mid+1,R,newl,newr,dep+1,k-cnt); 51 } 52 } 53 54 int main() { 55 int n,m; 56 while(scanf("%d%d",&n,&m)==2) { 57 clc(tree,0); 58 for(int i=1; i<=n; i++) { 59 scanf("%d",&tree[0][i]); 60 sorted[i]=tree[0][i]; 61 } 62 sort(sorted+1,sorted+1+n); 63 build(1,n,0); 64 int s,t,k; 65 while(m--) { 66 scanf("%d%d%d",&s,&t,&k); 67 printf("%d\n",query(1,n,s,t,0,k)); 68 } 69 } 70 return 0; 71 }
还有另外一种模板,记录比当前元素还小的元素和。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #define clc(a,b) sizeof(a,b,sizeof(a)) 6 #define LL long long 7 using namespace std; 8 const int N=1e5+5; 9 int p; 10 LL sum=0; 11 int sorted[N]; //对原来集合中的元素排序后的值 12 struct node { 13 int valu[N]; //val记录第k层当前位置元素的值 14 int num[N]; //num记录元素所在区间的当前位置之前进入左孩子的个数 15 LL sum[N]; //sum记录比当前元素小的元素的和 16 } t[20]; 17 18 void build(int lft,int rht,int ind) { 19 if(lft==rht) return; 20 int mid=lft+(rht-lft)>>1; 21 int isame=mid-lft+1,same=0; 22 /* isame用来标记和中间值val_mid相等的,且分到左孩子的数的个数 23 初始时,假定当前区间[lft,rht]有mid-lft+1个和valu_mid相等。 24 先踢掉中间值小的,剩下的就是要插入到左边的 25 */ 26 for(int i=lft; i<=rht; i++) 27 if(t[ind].valu[i]<sorted[mid]) isame--; 28 int ln=lft,rn=mid+1; 29 for(int i=lft; i<=rht; i++) { 30 if(i==lft) { //初始一个子树 31 t[p].num[i]=0; 32 t[p].sum[i]=0; 33 } else { //初始区间下一个节点 34 t[p].num[i]=t[p].num[i-1]; 35 t[p].sum[i]=t[p].sum[i-1]; 36 } 37 /* 如果大于,肯定进入右孩子,否则判断是否还有相等的应该进入左孩子的, 38 没有,直接进入右孩子,否则进入左孩子,同时更新节点的sum域和num域 39 */ 40 if(t[p].valu[i]<sorted[mid]) { 41 t[p].num[i]++; 42 t[p].sum[i]+=t[p].valu[i]; 43 t[p+1].valu[ln++]=t[p].valu[i]; 44 } else if(t[p].valu[i]>sorted[mid]) 45 t[p+1].valu[rn++]=t[p].valu[i]; 46 else { 47 if(same<isame) { 48 same++; 49 t[p].num[i]++; 50 t[p].sum[i]+=t[p].valu[i]; 51 t[p+1].valu[ln++]=t[p].valu[i]; 52 } else { 53 t[p+1].valu[rn++]=t[p].valu[i]; 54 } 55 } 56 } 57 build(lft,mid,ind+1); 58 build(mid+1,rht,ind+1); 59 } 60 61 int query(int a,int b,int k,int p,int lft,int rht) { 62 if(lft==rht) return t[p].valu[a]; 63 /*到达叶子结点就找到该元素,返回 64 S 记录区间[a,b]中进入左孩子的元素的个数 65 SS 记录区间[lft,a-1]中进入左孩子的元素的个数 66 SSS 记录区间[a,b]中小于第k大的元素的值和 67 B2 表示[lft,a-1]中分到右孩子的个数 68 BB 表示[a,b]中分到右孩子的个数 69 */ 70 int s,ss,b2,bb,mid=lft+(rht-lft)/2; 71 double sss=0; 72 if(a==lft) { //端点重合的情况,单独考虑 73 s = t[p].num[b]; 74 ss = 0; 75 sss = t[p].sum[b]; 76 } else { 77 s = t[p].num[b] - t[p].num[a-1]; 78 ss = t[p].num[a-1]; 79 sss = t[p].sum[b] - t[p].sum[a-1]; 80 } 81 if(s>=k) { //进入左孩子,同时更新区间端点值。 82 a = lft + ss;// 83 b = lft + ss + s - 1; 84 return query(a, b, k, p+1, lft, mid); 85 } else { 86 bb = a - lft - ss; 87 b2 = b - a - 1 - s; 88 a = mid + bb + 1; 89 b = mid + bb + b2; 90 sum += sss; 91 return query(a,b,k-s,p+1,mid+1,rht); 92 } 93 }