山东省第四届ACM大学生程序设计竞赛-Boring Counting(划分树-二分查找)
2016-05-07 20:54
459 查看
Boring Counting
Time Limit: 3000ms Memory limit: 65536K 有疑问?点这里^_^题目描述
In this problem you are given a number sequence P consisting of N integer and Pi is the ith element in the sequence. Now you task is to answer a list of queries,for each query, please tell us among [L, R], how many Pi is not less than A and not greater than B( L<= i <= R). In other words, your task is to count the number of Pi (L <= i <= R, A <= Pi <= B).
输入
In the first line there is an integer T (1 < T <= 50), indicates the number of test cases.For each case, the first line contains two numbers N and M (1 <= N, M <= 50000), the size of sequence P, the number of queries. The second line contains N numbers Pi(1 <= Pi <= 10^9), the number sequence P. Then there are M lines, each line contains four
number L, R, A, B(1 <= L, R <= n, 1 <= A, B <= 10^9)
输出
For each case, at first output a line ‘Case #c:’, c is the case number start from 1. Then for each query output a line contains the answer.示例输入
1 13 5 6 9 5 2 3 6 8 7 3 2 5 1 4 1 13 1 10 1 13 3 6 3 6 3 6 2 8 2 8 1 9 1 9
示例输出
Case #1: 13 7 3 6 9
提示
来源
2013年山东省第四届ACM大学生程序设计竞赛题目意思:
求每组数中,在l~r的范围内,有多少个数的值在a~b的范围内。解题思路:
划分树+二分查找。之前写过遍历查找直接超时……用二分的效率很高。
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #define N 50050 using namespace std; int sorted ; //排序完的数组 int toleft[30] ; //toleft[i][j]表示第i层从1到k有多少个数分入左边 int tree[30] ; //表示每层每个位置的值 int n; void building(int l,int r,int dep) { if(l==r) return; int mid = (l+r)>>1; int temp = sorted[mid]; int i,sum=mid-l+1; //表示等于中间值而且被分入左边的个数 for(i=l; i<=r; i++) if(tree[dep][i]<temp) sum--; int leftpos = l; int rightpos = mid+1; for(i=l; i<=r; i++) { if(tree[dep][i]<temp) //比中间的数小,分入左边 tree[dep+1][leftpos++]=tree[dep][i]; else if(tree[dep][i]==temp&&sum>0) //等于中间的数值,分入左边,直到sum==0后分到右边 { tree[dep+1][leftpos++]=tree[dep][i]; sum--; } else //右边 tree[dep+1][rightpos++]=tree[dep][i]; toleft[dep][i] = toleft[dep][l-1] + leftpos - l; //从1到i放左边的个数 } building(l,mid,dep+1); building(mid+1,r,dep+1); } //查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间 int query(int L,int R,int l,int r,int dep,int k) { if(l==r) return tree[dep][l]; int mid = (L+R)>>1; int cnt = toleft[dep][r] - toleft[dep][l-1]; //[l,r]中位于左边的个数 if(cnt>=k) { int newl = L + toleft[dep][l-1] - toleft[dep][L-1]; //L+要查询的区间前被放在左边的个数 int newr = newl + cnt - 1; //左端点加上查询区间会被放在左边的个数 return query(L,mid,newl,newr,dep+1,k); } else { int newr = r + (toleft[dep][R] - toleft[dep][r]); int newl = newr - (r-l-cnt); return query(mid+1,R,newl,newr,dep+1,k-cnt); } } int MAXA(int L,int R,int l,int r,int a) //二分枚举 { int ans=-1; while(l<=r) { int mid = (l+r)>>1; int res = query(1,n,L,R,0,mid); if(res>=a) //直到找到最左边的那个等于a的结果 { r = mid - 1; ans = mid; } else l = mid + 1; } return ans; } int MINB(int L,int R,int l,int r,int b) { int ans=0; while(l<=r) { int mid = (l+r)>>1; int res = query(1,n,L,R,0,mid); if(res>b) //直到找到最后边的大于b的结果 { r = mid - 1; ans = mid; } else l = mid + 1; } if(!ans) return r; return ans-1; } int main() { int t,cas = 1; scanf("%d",&t); while(t--) { int m; scanf("%d%d",&n,&m); int i; for(i=1; i<=n; i++) { scanf("%d",&tree[0][i]); sorted[i] = tree[0][i]; } sort(sorted+1,sorted+1+n); building(1,n,0); int l,r,a,b; printf("Case #%d:\n",cas++); while(m--) { scanf("%d%d%d%d",&l,&r,&a,&b); int x = 1; int y = r-l+1; int cnt1 = MAXA(l,r,x,y,a); int cnt2 = MINB(l,r,x,y,b); if(cnt1==-1) { printf("0\n"); continue; } printf("%d\n",cnt2-cnt1+1); } } return 0; } /************************************** Problem id : SDUT OJ 2610 User name : 吃花的栗鼠 Result : Accepted Take Memory : 7260K Take Time : 1230MS Submit Time : 2016-05-07 20:27:46 **************************************/
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++高级程序员成长之路
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- 解析C++中派生的概念以及派生类成员的访问属性