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:\n",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\n",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:\n",++t);
56         int s,t,k;
57         while(m--){
58             scanf("%d%d",&s,&t);
59             k=(t-s+2)/2;
60             printf("%d\n",query(1,n,s,t,0,k));
61         }
62     }
63     return 0;
64 }

划分树

时间: 2024-10-25 10:19:27

HDU 4251 --- 主席树(划分树是正解)的相关文章

归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665

如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k大 ,,,, 这个问题的通用算法是 划分树,, 说白一点就是把快速排序的中间结果存起来, 举个栗子 原数列 4 1 8 2 6 9 5 3 7 sorted 1 2 3 4 5 6 7 8 9 ........................... qs[0] 4 1 8 2 6 9 5 3 7 q

hdu 3473 Minimum Sum(划分树-sum操作)

划分树.只是考虑求当前区间大于第k值的值得和,和小于第k值的和.显然可以在查询的时候直接搞出来.sum[d][i]表示第d层子区间l,r种l-i的和.写错了一个下标,检查了半辈子... #include<algorithm> #include<iostream> #include<cstring> #include<vector> #include<cstdio> #include<cmath> #include<queue&g

hdu 2665 Kth number(划分树)

题意:给定一列数,每次查询区间[s,t]中的第k大: 参考:http://www.cnblogs.com/kane0526/archive/2013/04/20/3033212.html http://www.cnblogs.com/kuangbin/archive/2012/08/14/2638829.html 思路:快排思想+线段树=划分树,也就是树的每一层都按规则划分: 对于本题,建树前,保存原数列排序后的数列,原数列作为树的顶层: 建树时,考虑当前区间中的中间值,若大于中间值,在下一层中

HDU 3473 Minimum Sum 划分树,数据结构 难度:1

http://acm.hdu.edu.cn/showproblem.php?pid=3473 划分树模板题目,需要注意的是划分树的k是由1开始的 划分树: 参考:http://blog.csdn.net/shiqi_614/article/details/8041390 划分树的定义 划分树定义为,它的每一个节点保存区间[lft,rht]所有元素,元素顺序与原数组(输入)相同,但是,两个子树的元素为该节点所有元素排序后(rht-lft+1)/2个进入左子树,其余的到右子树,同时维护一个num域,

hdu 3473 Minimum Sum 划分树的应用

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3473 Minimum Sum Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3427    Accepted Submission(s): 787 Problem Description You are given N positive i

HDU 4417 Super Mario 划分树/树状数组

题目大意:给定一个序列,求区间内小于等于某数的元素数量 首先区间排名一看就是划分树 不过和第k小不一样 我们需要做一些处理 第一种处理方式是二分答案 然后转换成区间第k小 好方法我喜欢 但是这里说的不是这种方法 首先建树,然后对于每个询问,我们讨论k与a[mid]的关系 若k<a[mid],则右子树一定没有小于等于k的数,我们进入左子树查找 若k>=a[mid],则左子树内一定所有数都小于等于k,于是我们将查询区间中进入左子树的元素的数量记入ans,然后查找右区间 递归退出条件是查询区间为空或

hdu3473 线段树 划分树

1 //Accepted 28904 KB 781 ms 2 //划分树 3 //所求x即为l,r区间排序后的中位数t 4 //然后求出小于t的数的和sum1,这个可以用划分树做 5 //求出整个区间的和sum,可以用O(1)的数组做 6 //ans=(k-1)*t-sum1+sum-sum1-(l-r+1-k+1)*t 7 #include <cstdio> 8 #include <cstring> 9 #include <iostream> 10 #include

HDU 3473 Minimum Sum 划分树

题目大意:给定一个序列,每次询问给出一个区间,我们需要选择一个数,这个数到区间内所有数的距离之和最小,求最小和 由绝对值不等式可得 当我们选择的这个数是中位数的时候距离和最小 于是这题就转换成了区间第k小 但是这题求的是最小和 于是我们做一个处理 我们多维护一个sum域 sum[i]表示[l,i]区间内划分到左子树中元素的总和 然后我们每次查询第k小时 如果我们进入的是右子树 就把划分到左子树中的元素和累加到left_sum上 然后用前缀和计算出区间的和 计算出right_sum 最后的结果就是

HDU 4417 Super Mario(划分树问题求不大于k的数有多少)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3625    Accepted Submission(s): 1660 Problem Description Mario is world-famous plumber. His “burly” figure and amazing jumping ability