HDU 4251 --- 主席树(划分树是正解)

题意:查询区间中位数

思路:模板题,相当于区间第K大的数,主席树可以水过,但划分树是正解。但还没搞明白划分树,先上模板

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cmath>
  4 #include <cstring>
  5 #include <cstdlib>
  6 #include <string>
  7 #include <vector>
  8 #include <algorithm>
  9 #include <set>
 10 #define repu(i,a,b) for(int i=a;i<b;i++)
 11 using namespace std;
 12 #define N 1000010
 13 #define ll long long
 14 #define _cle(m, a) memset(m, a, sizeof(m))
 15 const int MAXN = 100010;
 16 const int M = MAXN * 30;
 17 int n,q,m,tot;
 18 int a[MAXN], t[MAXN];
 19 int T[MAXN], lson[M], rson[M], c[M];
 20 void Init_Hash()
 21 {
 22     for(int i = 1; i <= n; i++)
 23         t[i] = a[i];
 24     sort(t+1,t+1+n);
 25     m = unique(t+1,t+1+n) -t-1;
 26 }
 27 int build(int l, int r)
 28 {
 29     int root = tot++;
 30     c[root] = 0;
 31     if(l != r)
 32     {
 33         int mid = (l+r)>>1;
 34         lson[root] = build(l,mid);
 35         rson[root] = build(mid+1,r);
 36     }
 37     return root;
 38 }
 39 int Hash(int x)
 40 {
 41     return lower_bound(t+1,t+1+m,x) - t;
 42 }
 43 int update(int root, int pos, int val)
 44 {
 45     int newroot = tot++, tmp = newroot;
 46     c[newroot] = c[root] + val;
 47     int l = 1, r = m;
 48     while(l < r)
 49     {
 50         int mid = (l+r)>>1;
 51         if(pos <= mid)
 52         {
 53             lson[newroot] = tot++;
 54             rson[newroot] = rson[root];
 55             newroot = lson[newroot];
 56             root = lson[root];
 57             r = mid;
 58         }
 59         else
 60         {
 61             rson[newroot] = tot++;
 62             lson[newroot] = lson[root];
 63             newroot = rson[newroot];
 64             root = rson[root];
 65             l = mid+1;
 66         }
 67         c[newroot] = c[root] + val;
 68     }
 69     return tmp;
 70 }
 71 int query(int left_root, int right_root, int k)
 72 {
 73     int l = 1, r = m;
 74     while( l < r)
 75     {
 76         int mid = (l+r)>>1;
 77         if(c[lson[left_root]] -c[lson[right_root]] >= k )
 78         {
 79             r = mid;
 80             left_root = lson[left_root];
 81             right_root = lson[right_root];
 82         }
 83         else
 84         {
 85             l = mid + 1;
 86             k -= c[lson[left_root]] - c[lson[right_root]];
 87             left_root = rson[left_root];
 88             right_root = rson[right_root];
 89         }
 90     }
 91     return l;
 92 }
 93 int main()
 94 {
 95 //freopen("in.txt","r", stdin);
 96 //freopen("out.txt","w", stdout);
 97     int kase = 1;
 98     while(scanf("%d",&n) == 1)
 99     {
100         tot = 0;
101         for(int i = 1; i <= n; i++)
102             scanf("%d",&a[i]);
103         Init_Hash();
104         T[n+1] = build(1,m);
105         for(int i = n; i ; i--)
106         {
107             int pos = Hash(a[i]);
108             T[i] = update(T[i+1],pos,1);
109         }
110         printf("Case %d:
",kase++);
111         scanf("%d",&q);
112         while(q--)
113         {
114             int l,r,k;
115             scanf("%d%d",&l,&r);
116             k = (r-l)/2+1;
117             printf("%d
",t[query(T[l],T[r+1],k)]);
118         }
119     }
120     return 0;
121 }
主席树

先上KB模板

 1 //划分树
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int MAXN=100010;
 7 int tree[20][MAXN];//表示每层每个位置的值
 8 int sorted[MAXN];//已经排序好的数
 9 int toleft[20][MAXN];//toleft[p][i]表示第i层从1到i有数分入左边
10 void build(int l,int r,int dep){
11     if(l==r)return;
12     int mid=(l+r)>>1;
13     int same=mid-l+1;//表示等于中间值而且被分入左边的个数
14     for(int i=l;i<=r;i++)//注意是l,不是one
15         if(tree[dep][i]<sorted[mid]) same--;
16     int lpos=l;
17     int rpos=mid+1;
18     for(int i=l;i<=r;i++){
19         if(tree[dep][i]<sorted[mid]) tree[dep+1][lpos++]=tree[dep][i];
20         else if(tree[dep][i]==sorted[mid]&&same>0){
21             tree[dep+1][lpos++]=tree[dep][i];
22             same--;
23         }
24         else tree[dep+1][rpos++]=tree[dep][i];
25         toleft[dep][i]=toleft[dep][l-1]+lpos-l;
26     }
27     build(l,mid,dep+1);
28     build(mid+1,r,dep+1);
29 }
30 //查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间
31 int query(int L,int R,int l,int r,int dep,int k){
32     if(l==r)return tree[dep][l];
33     int mid=(L+R)>>1;
34     int cnt=toleft[dep][r]-toleft[dep][l-1];
35     if(cnt>=k){
36         int newl=L+toleft[dep][l-1]-toleft[dep][L-1];int newr=newl+cnt-1;
37         return query(L,mid,newl,newr,dep+1,k);
38     }
39     else{
40         int newr=r+toleft[dep][R]-toleft[dep][r],newl=newr-(r-l-cnt);
41         return query(mid+1,R,newl,newr,dep+1,k-cnt);
42     }
43 }
44 int main(){
45     int n,m,t=0;
46     while(~scanf("%d",&n)){
47         memset(tree,0,sizeof(tree));
48         for(int i=1;i<=n;i++){
49             scanf("%d",&tree[0][i]);
50             sorted[i]=tree[0][i];
51         }
52         sort(sorted+1,sorted+n+1);
53         build(1,n,0);
54         scanf("%d",&m);
55         printf("Case %d:
",++t);
56         int s,t,k;
57         while(m--){
58             scanf("%d%d",&s,&t);
59             k=(t-s+2)/2;
60             printf("%d
",query(1,n,s,t,0,k));
61         }
62     }
63     return 0;
64 }
划分树
原文地址:https://www.cnblogs.com/ACMERY/p/4507196.html