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
",query(1,n,s,t,0,k));
68         }
69     }
70     return 0;
71 }
View Code

还有另外一种模板,记录比当前元素还小的元素和。

 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 }
View Code
原文地址:https://www.cnblogs.com/ITUPC/p/5277257.html