HDU3473--Minimum Sum(静态区间第k大)

Minimum Sum

Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3047    Accepted Submission(s): 701

Problem Description

You are given N positive integers, denoted as x0, x1 ... xN-1. Then give you some intervals [l, r]. For each interval, you need to find a number x to make as small as possible!

Input

The first line is an integer T (T <= 10), indicating the number of test cases. For each test case, an integer N (1 <= N <= 100,000) comes first. Then comes N positive integers x (1 <= x <= 1,000, 000,000) in the next line. Finally, comes an integer Q (1 <= Q <= 100,000), indicting there are Q queries. Each query consists of two integers l, r (0 <= l <= r < N), meaning the interval you should deal with.

Output

For the k-th test case, first output “Case #k:” in a separate line. Then output Q lines, each line is the minimum value of  . Output a blank line after every test case.

Sample Input

2

5

3 6 2 2 4

2

1 4

0 2

2

7 7

2

0 1

1 1

Sample Output

Case #1:

6

4

Case #2:

0

0

区间第k大的题目一般两种做法,,划分树 主席树,不过貌似划分树只支持静态区间第k大。

这道题由于内存限制 只能用划分树。。具体就是 先找出区间[l,r]内的中位数,(为什么是中位数,大白书貌似有介绍),然后直接求和。

MLE到死啊,,注释memset部分之后就过了,不知道为什么
划分树代码:

  1 #include <set>
  2 #include <map>
  3 #include <cmath>
  4 #include <ctime>
  5 #include <queue>
  6 #include <stack>
  7 #include <cstdio>
  8 #include <string>
  9 #include <vector>
 10 #include <cstdlib>
 11 #include <cstring>
 12 #include <iostream>
 13 #include <algorithm>
 14 using namespace std;
 15 typedef unsigned long long ull;
 16 typedef long long ll;
 17 const int inf = 0x3f3f3f3f;
 18 const double eps = 1e-8;
 19 const int maxn = 100010;
 20 int sorted[maxn],tree[20][maxn],toleft[20][maxn];   //toleft[dep][i]表示第dep层1-i中进入左子树元素的个数
 21 ll leftsum[20][maxn], sum[maxn];              //leftsum[dep][i]表示第dep层1-i中进入左子树元素的和
 22
 23 void build (int l,int r,int dep)
 24 {
 25     if (l == r)
 26         return;
 27     int mid = (l + r) >> 1;
 28     int same = mid - l + 1;
 29     for (int i = l; i <= r; i++)
 30         if (tree[dep][i] < sorted[mid])
 31             same--;
 32     int lpos = l,rpos = mid + 1;
 33     for (int i = l; i <= r; i++)
 34     {
 35         if (tree[dep][i] < sorted[mid])
 36         {
 37             tree[dep+1][lpos++] = tree[dep][i];
 38             leftsum[dep][i] = leftsum[dep][i-1] + tree[dep][i];
 39         }
 40         else if (tree[dep][i] == sorted[mid] && same > 0)
 41         {
 42             tree[dep+1][lpos++] = tree[dep][i];
 43             leftsum[dep][i] = leftsum[dep][i-1] + tree[dep][i];
 44             same--;
 45         }
 46         else
 47         {
 48             tree[dep+1][rpos++] = tree[dep][i];
 49             leftsum[dep][i] = leftsum[dep][i-1];
 50         }
 51         toleft[dep][i] = toleft[dep][l-1] + lpos - l;
 52     }
 53     build (l,mid,dep+1);
 54     build (mid+1,r,dep+1);
 55 }
 56 int lnum,rnum;
 57 ll lsum,rsum;
 58 int query(int l,int r,int dep,int ua,int ub,int k)
 59 {
 60     if (ua == ub)
 61         return tree[dep][ua];
 62     int mid = (l + r) >> 1;
 63     int cnt = toleft[dep][ub] - toleft[dep][ua-1];
 64     if (cnt >= k)
 65     {
 66         int newl = l + toleft[dep][ua-1] - toleft[dep][l-1];
 67         int newr = newl + cnt - 1;
 68         return query(l,mid,dep+1,newl,newr,k);
 69     }
 70     else
 71     {
 72         int newr = ub + toleft[dep][r] - toleft[dep][ub];
 73         int newl = newr - (ub - ua - cnt);
 74         lnum += cnt;
 75         lsum += leftsum[dep][ub] - leftsum[dep][ua-1];
 76         return query(mid+1,r,dep+1,newl,newr,k-cnt);
 77     }
 78 }
 79
 80 int main(void)
 81 {
 82     #ifndef ONLINE_JUDGE
 83         freopen("in.txt","r",stdin);
 84     #endif
 85     int T, cas = 1;
 86     scanf ("%d",&T);
 87     while (T--)
 88     {
 89         int n,m;
 90         scanf ("%d",&n);
 91         sum[0] = 0;
 92         for (int i = 1; i <= n; i++)
 93         {
 94             scanf ("%d",&tree[0][i]);
 95             sum[i] = sum[i-1] + tree[0][i];
 96             sorted[i] = tree[0][i];
 97         }
 98         sort(sorted+1,sorted+n+1);
 99         build (1,n,0);
100         scanf ("%d",&m);
101         printf ("Case #%d:\n",cas++);
102         while (m--)
103         {
104             int u, v;
105             scanf ("%d%d",&u,&v);
106             u++,v++;
107             lnum = 0;
108             lsum = 0;
109             int mid_num = query(1,n,0,u,v,(v-u)/2+1);             //中位数
110             rnum = (v - u + 1 - lnum);                            // u~v 区间内大于mid_num的个数
111             rsum = (sum[v] - sum[u-1] - lsum);                    //u~v    区间内大于mid_num的数的和
112             ll ans = rsum - lsum + mid_num * (lnum - rnum);
113             printf("%I64d\n",ans);
114         }
115         printf("\n");
116     }
117     return 0;
118 }

主席树部分代码:(主席树会MLE,已经和划分树代码对拍过,应该没问题)

  1 typedef long long ll;
  2 const int maxn = 1e5+10;
  3 int n,q,tot;
  4
  5 //主席树部分
  6 int lson[maxn*20],rson[maxn*20],c[maxn*20],tree[maxn];
  7
  8 ll sum[maxn*20];
  9 int build (int l,int r)
 10 {
 11     int root = tot++;
 12     c[root] = 0;
 13     sum[root] = 0;
 14     if (l != r)
 15     {
 16         int mid = (l + r) >> 1;
 17         lson[root] = build(l,mid);
 18         rson[root] = build(mid+1,r);
 19     }
 20     return root;
 21 }
 22 int update(int root,int pos,int val,int k)
 23 {
 24     int newroot = tot++;
 25     int tmp = newroot;
 26     int l = 1, r = n;
 27     c[newroot] = c[root] + val;
 28     sum[newroot] = sum[root] + k;
 29     while (l < r)
 30     {
 31         int mid = (l + r) >> 1;
 32         if (pos <= mid)
 33         {
 34             rson[newroot] = rson[root];
 35             root = lson[root];
 36             lson[newroot] = tot++;
 37             newroot = lson[newroot];
 38             r = mid;
 39         }
 40         else
 41         {
 42             lson[newroot] = lson[root];
 43             root = rson[root];
 44             rson[newroot] = tot++;
 45             newroot = rson[newroot];
 46             l = mid + 1;
 47         }
 48         c[newroot] = c[root] + val;
 49         sum[newroot] = sum[root] + k;
 50     }
 51     return tmp;
 52 }
 53 ll lsum,rsum;                       //lsum小于中位数的和,rsum大于中位数的和
 54 ll query(int root,int l,int r,int ua,int ub)                  //查询1-root 大于ua小于ub的和
 55 {
 56     if (ub < ua)
 57         return 0;
 58     if (ua <= l && ub >= r)
 59     {
 60         return sum[root];
 61     }
 62     int mid = (l + r) >> 1;
 63     ll t1 = 0,t2 = 0;
 64     if (ua <= mid)
 65         t1 = query(lson[root],l,mid,ua,ub);
 66     if (ub > mid)
 67         t2 = query(rson[root],mid+1,r,ua,ub);
 68     return t1 + t2;
 69 }
 70 int query1(int root,int l,int r,int ua,int ub)               //查询1-root  在ua ub 之间的数的个数
 71 {
 72     if (ub < ua)
 73         return 0;
 74     if (ua <= l && ub >= r)
 75     {
 76         return c[root];
 77     }
 78     int mid = (l + r) >> 1;
 79     int t1 = 0,t2 = 0;
 80     if (ua <= mid)
 81         t1 = query1(lson[root],l,mid,ua,ub);
 82     if (ub > mid)
 83         t2 = query1(rson[root],mid+1,r,ua,ub);
 84     return t1 + t2;
 85 }
 86 int query(int left,int right,int k)                                 //查询left right区间第k大
 87 {
 88     int l_root = tree[left-1];
 89     int r_root = tree[right];
 90     int l = 1, r = n;
 91     while (l < r)
 92     {
 93         int mid = (l + r) >> 1;
 94         int tmp = c[lson[r_root]] - c[lson[l_root]];
 95         if (tmp <= k)
 96         {
 97             k -= tmp;
 98             r_root = rson[r_root];
 99             l_root = rson[l_root];
100             l = mid + 1;
101         }
102         else
103         {
104             l_root = lson[l_root];
105             r_root = lson[r_root];
106             r = mid;
107         }
108     }
109     return l;
110 }
111 int vec[maxn],rel[maxn],idx;
112 //离散化
113 inline void init_hash()
114 {
115     sort(vec,vec+idx);
116     idx = unique(vec,vec+idx) - vec;
117 }
118 inline int _hash(ll x)
119 {
120     return lower_bound(vec,vec+idx,x) - vec + 1;
121 }
122
123
124 int main(void)
125 {
126     #ifndef ONLINE_JUDGE
127         freopen("in.txt","r",stdin);
128         freopen("wa.txt","w",stdout);
129     #endif
130     int t,cas = 1;
131     scanf ("%d",&t);
132     while (t--)
133     {
134         memset(sum,0,sizeof(sum));
135         scanf ("%d",&n);
136         idx = tot = 0;
137         for (int i = 1; i<= n; i++)
138         {
139             //scanf ("%d",a+i);
140             scanf ("%d",tree+i);
141             vec[idx++] = tree[i];
142         }
143         init_hash();
144         tree[0] = build(1,n);
145         for (int i = 1; i <= n; i++)
146         {
147             int tmp = _hash(tree[i]);
148             rel[tmp] = tree[i];
149
150             tree[i] = update(tree[i-1],tmp,1,tree[i]);
151             //tree[i] = tmp2;
152         }
153         scanf ("%d",&q);
154         printf("Case #%d:\n",cas++);
155         while (q--)
156         {
157             int u,v;
158             scanf ("%d%d",&u,&v);
159             u++,v++;
160             int vir_mid = query(u,v,(v-u)/2);
161             int mid_num = rel[vir_mid];                //中位数
162             lsum = query(tree[u-1],1,n,1,vir_mid-1);
163             rsum = query(tree[u-1],1,n,vir_mid+1,n);
164             ll x1 = lsum, x2 = rsum;
165
166             lsum = query(tree[v],1,n,1,vir_mid-1);
167             rsum = query(tree[v],1,n,vir_mid+1,n);
168             int lnum = query1(tree[v],1,n,1,vir_mid - 1) - query1(tree[u-1],1,n,1,vir_mid - 1);
169             int rnum = query1(tree[v],1,n,vir_mid + 1,n) - query1(tree[u-1],1,n,vir_mid + 1,n);
170             printf("%I64d\n",rsum - x2 - (lsum - x1) +  (lnum - rnum) * mid_num);
171         }
172         printf("\n");
173     }
174     return 0;
175
176 }
时间: 2024-12-09 22:33:47

HDU3473--Minimum Sum(静态区间第k大)的相关文章

主席树(静态区间第k大)

前言 如果要求一些数中的第k大值,怎么做? 可以先就这些数离散化,用线段树记录每个数字出现了多少次. ... 那么考虑用类似的方法来求静态区间第k大. 原理 假设现在要有一些数 我们可以对于每个数都建一棵新的线段树,用来记录出现每个数字出现了多少次的前缀和. 那么假设要求区间[l,r]的第k大,将第r棵线段树减去第l-1棵线段树,像上面求所有数的第k大一样来求就可以了. 但是,对于每一个数都建一个线段树显然会爆空间. 现在考虑如何节约空间. 假设现在有四个数1 4 2 3,依次加入,可以这样处理

POJ2104-- K-th Number(主席树静态区间第k大)

[转载]一篇还算可以的文章,关于可持久化线段树http://finaltheory.info/?p=249 无修改的区间第K大 我们先考虑简化的问题:我们要询问整个区间内的第K大.这样我们对值域建线段树,每个节点记录这个区间所包含的元素个数,建树和查询时的区间范围用递归参数传递,然后用二叉查找树的询问方式即可:即如果左边元素个数sum>=K,递归查找左子树第K大,否则递归查找右子树第K – sum大,直到返回叶子的值. 现在我们要回答对于区间[l, r]的第K大询问.如果我们能够得到一个插入原序

poj2761静态区间第k大

例题:poj2761 题目要求:给定一个长度为n的序列,给定m个询问,每次询问求[l,r]区间内的第k大: 对于这道题目来说,很多算法都可以使用,比如说树套树(一个负责划分区间,一个负责维护这段区间内的信息),主席树等: 对这道题我使用的是主席树: 主席树对付区间第k大是很优秀的,代码短,而且常数小: 主席树的主要功能是,建立n颗范围是1-i的权值线段树,对两颗线段树做差,就可以任意一个区间内的权值线段树: 详细看代码: #include<iostream> #include<cstdi

静态区间第k大 树套树解法

然而过不去你谷的模板 思路: 值域线段树\([l,r]\)代表一棵值域在\([l,r]\)范围内的点构成的一颗平衡树 平衡树的\(BST\)权值为点在序列中的位置 查询区间第\(k\)大值时 左区间在\([l,r]\)范围内的树的大小与\(k\)比较 大了进去,小了减掉换一边 关于建树 递归建估计是\(O(nlog^2n)\)的 Code: #include <cstdio> #include <cstdlib> #include <algorithm> #includ

[poj 2104]主席树+静态区间第k大

题目链接:http://poj.org/problem?id=2104 主席树入门题目,主席树其实就是可持久化权值线段树,rt[i]维护了前i个数中第i大(小)的数出现次数的信息,通过查询两棵树的差即可得到第k大(小)元素. #include<cstdio> #include<vector> #include<algorithm> using namespace std; #define lson(i) node[i].lson #define rson(i) node

HDU3727--Jewel (主席树 静态区间第k大)

Jewel Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 985    Accepted Submission(s): 247 Problem Description Jimmy wants to make a special necklace for his girlfriend. He bought many beads with

主席树——求静态区间第k大

例题:poj2104 http://poj.org/problem?id=2104 讲解:http://blog.sina.com.cn/s/blog_6022c4720102w03t.html http://seter.is-programmer.com/posts/31907.html 刚刚根据以上2篇博客看懂,还没到写讲解的水平 #include<cstdio> #include<algorithm> using namespace std; const int N=1000

手撸主席树模板[静态区间第k大]

#include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int maxn =2e5; int tot,n,m; int sum[(maxn << 5) + 10],rt[maxn + 10],ls[(maxn << 5) + 10],rs[(maxn << 5) + 10]; int a[maxn + 10],ind[ma

POJ 2104 静态找区间第k大

静态区间第k大的问题,往往可以利用主席树来解决 这是主席树的第一道题 主席树大概可以理解为在n个节点上都建立一棵线段树,但是想想会超出内存 每一个节点保存的线段树都记录当前整段前缀区间的信息 但是因为每次添加后一个节点,那么他除了当前节点位置需要更新之外,其他的位置都可以保持跟上一棵节点对应的线段树一致,那么为了缩小内存, 将那些不需要改变的点的指针指向上一棵树对应的节点即可,其他多生成的节点也就是需要更新的节点,最多不超过log2n个,所以最后产生的线段树的 点的个数大概在nlogn的大致范围