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大学生程序设计竞赛

 
 
  划分树 + 二分(据说归并树也可以做)
 
  题意:给你T组测试数据,每一组测试数据开始有两个整数N和M。分别表示下一行要输入N个整数,后面接着有M行询问,每行询问有4个整数 L,R,A,B,表示在区间 [L,R] 中查找 A<=Pi<=B 的数有多少个,输出数的个数。
 
  思路
  这道题最直接的思路是对要求的区间排一下序,然后依次判断比较,记录下在范围[A,B]之内的数有多少个,即为结果。但是M即询问的次数范围在[1,50000],如果每询问一次都要排一次序的话,时间消耗过大,所以上来直接pass掉这种思路。
  因为这道题是基于区间查询,所以接下来我很自然的就想到了“线段树”。线段树的每一个节点代表一个区间,节点中存储的值为区间的特定值,在这里我用节点区间中的最大值,最小值,即数值范围,来表示这个特定值。查询的时候,先找到要找的区间,然后递归下去寻找这个区间内的值有多少个是在数值范围内。如果找到这个区间,发现这个区间的最小值都比[A,B]中的上限B要大,或者最大值都比下限A要小。说明这个区间内没有一个是符合要求的,这个节点代表的整个子树就不用找了。如此,会节约不少时间。
  但是线段树的方法也会超时,比赛的时候看到TLE可是苦思冥想了好久,想了各种优化的方法。无奈,还是没能用线段树解出这道题。
  后来比赛结束后查题解,才知道原来还有"划分树"这种神奇的东西。划分树是基于线段树的,但是我感觉它只继承了线段树“基于区间”的特点,其他的感觉我觉得倒没有什么联系。划分树主要是用来解决“区间第k大的数”问题。下面就让我具体的来讲讲如何用划分树解决这道题。
 
  大家需要先大体了解一下什么是“划分树”,它的作用是“快速求出某一区间内第k大的元素”,例如数列“1,2,3,4,5,6,7,8",区间[3,6]内第1大的值就是3。
  那么如何用这个特性求出某一区间内数值在[A,B]范围内的数有多少个呢?
  举个例子:数列“1,5,6,3,8,4,4,2”,L,R,A,B分别为3,6,4,7,即在区间[3,6]范围内查找数值在[4,7]的数有多少个。很显然,这里要查找的区间就是"6,3,8,4",4<=Pi<=7的数有2个。
  用划分树的思路来解决这个问题。先求出这个区间范围内第1大的数是多少,这里是3,拿它和下限 4(A) 来比较,比这个数小,说明不在这个数值范围内。然后求出第2大的数是4,和下限4(A)比较,在这个范围内,记录下它是第几大的数,标记为down,代表 >= 下限A的第一个数是第几大的数,这里为2(第二大的数)。同理,再依次和上限7(B)比较,求出第一个 <= 上限B的数是第几大的数,标记为up,代表 <= 上限B的数是第几大的数,这里为3。这之间包含了多少个数即为要求得的结果,即(up-down+1) 。
  刚才的思路利用划分树顺序查找锁定up和down的值,这样未免会太慢。这道题对速度的要求还是比较高的,所以这里就要用到“二分"的思想。什么是二分呢?举个例子,如果查询区间为[1,8],要求数值范围为[3,6]的数有多少个,先求down。可以先用中间那个数来和下限3比较,即用第4大的数和下限比较,如果比下限小,说明个要找的数在第4大的数右边,下一步再用4和8的中间值,即第6大的数和下限比较,最后得到down的值。同理可以求出up的值。这样一来,速度上又进行了一次优化。
 
  参考资料
 
  代码(带注释)
 #include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
#define MAXN 100005
struct Divide_tree{
int arr[MAXN]; //原数组
int sorted[MAXN]; //排序后数组
int sum[][MAXN]; //记录第i层1~j划分到左子树的元素个数(包括j)
int dat[][MAXN]; //记录第i层元素序列
void build(int c,int L,int R) //建树,主要是建立sum[][]和dat[][]数组
{
int mid = (L+R)>>;
int lsame = mid-L+; //lsame用来记录和中间值val_mid相等的,且可以分到左孩子的数的个数
//简单来说就是可以放入左孩子的,与中间值val_mid相等的数的个数
int lp=L,rp=mid+; //当前节点的左孩子和右孩子存数的起点
for(int i=L;i<mid;i++) //获得一开始的lsame
if(sorted[i]<sorted[mid])
lsame--;
for(int i=L;i<=R;i++){ //从前往后遍历一遍,
//确定当前节点区间内的所有元素的归属(放在左孩子或者放在右孩子)
if(i==L) sum[c][i]=;
else sum[c][i]=sum[c][i-];
if(dat[c][i]<sorted[mid]){ //当前元素比中间值val_mid小,放入左孩子
dat[c+][lp++] = dat[c][i];
sum[c][i]++;
}
else if(dat[c][i]>sorted[mid]) //当前元素比中间值val_mid大,放入右孩子
dat[c+][rp++] = dat[c][i];
else{ //当前元素值与中间值val_mid相等,根据lsame数判断放入左孩子还是右孩子
if(lsame){
lsame--;
sum[c][i]++;
dat[c+][lp++]=sorted[mid];
}
else{
dat[c+][rp++]=sorted[mid];
}
}
}
if(L==R) return ; //递归出口,遇到叶子节点
build(c+,L,mid); //递归进入左孩子区间
build(c+,mid+,R); //递归进入右孩子区间
}
int query(int c,int L,int R,int ql,int qr,int k)
{
//c为树的层数,L,R为当前节点的区间范围,ql,qr为查询的区间范围,k为查询范围内第k大的数
if(L==R) //递归出口,返回第k大的数
return dat[c][L];
int s; //记录[L,ql-1]中进入左孩子的元素的个数
int ss; //记录[ql,qr]中进入左孩子的元素的个数
int mid=(L+R)>>;
if(L==ql){ //端点重合的情况,单独考虑
s=;
ss=sum[c][qr];
}
else {
s=sum[c][ql-];
ss=sum[c][qr]-s;
}
if(k<=ss) //左孩子的元素个数大于k个,说明第k大的元素一定在左孩子区间中,到左孩子中查询
return query(c+,L,mid,L+s,L+s+ss-,k);
else
return query(c+,mid+,R,mid++ql-s-L,mid++qr-s-ss-L,k-ss);
}
};
Divide_tree tree;
int L,R,A,B;
int N,M;
int downbearch(int low,int high) //找到第一个比B小的数是 第几大的数(用划分树)
{
int mid = (low+high+)>>;
while(low<high){
if( tree.query(,,N,L,R,mid)<=B ) //查询第mid大的数是否比下界B小。
//如果mid比B小,说明要找的位置在mid右边,low应该右移,即
low = mid;
else //否则,说明要找的位置应该在mid左边,即
high = mid-;
mid = (low+high+)>>;
}
if( tree.query(,,N,L,R,mid)<=B ) //找到了
return mid;
else
return -;
}
int upbearch(int low,int high) //找到第一个比A大的数是 第几大的数(用划分树)
{
int mid = (low+high+)>>;
while(low<high){
if( tree.query(,,N,L,R,mid)>=A ) //查询第mid大的数是否比上界A大。
//如果mid比A大,说明要找的位置在mid左边,high应该左移,即
high = mid;
else //否则,说明要找的位置应该在mid右边,即
low = mid+;
mid = (low+high)>>;
}
if(tree.query(,,N,L,R,mid)>=A ) //找到了
return mid;
else
return -;
}
int main()
{
int i,Case,T;
scanf("%d",&T);
for(Case=;Case<=T;Case++){
scanf("%d%d",&N,&M);
for(i=;i<=N;i++){ //输入
scanf("%d",&tree.arr[i]);
tree.sorted[i]=tree.dat[][i]=tree.arr[i];
}
sort(tree.sorted+,tree.sorted+N+);
tree.build(,,N);
printf("Case #%d:\n",Case);
for(i=;i<=M;i++){
scanf("%d%d%d%d",&L,&R,&A,&B);
int up = downbearch(,R-L+);
int down = upbearch(,R-L+);
if(up==-||down==-||A>B||L>R)
printf("0\n");
else printf("%d\n",up-down+);
}
}
return ;
}

Freecode : www.cnblogs.com/yym2013

sdut 2610:Boring Counting(第四届山东省省赛原题,划分树 + 二分)的更多相关文章

  1. sdut 2603:Rescue The Princess(第四届山东省省赛原题,计算几何,向量旋转 + 向量交点)

    Rescue The Princess Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 Several days ago, a b ...

  2. SDUT 2610 Boring Counting(离散化+主席树区间内的区间求和)

    Boring Counting Time Limit: 3000MS Memory Limit: 65536KB Submit Statistic Discuss Problem Descriptio ...

  3. sdut 2416:Fruit Ninja II(第三届山东省省赛原题,数学题)

    Fruit Ninja II Time Limit: 5000MS Memory limit: 65536K 题目描述 Have you ever played a popular game name ...

  4. sdut 2413:n a^o7 !(第三届山东省省赛原题,水题,字符串处理)

    n a^o7 ! Time Limit: 1000MS Memory limit: 65536K 题目描述 All brave and intelligent fighters, next you w ...

  5. sdut 2162:The Android University ACM Team Selection Contest(第二届山东省省赛原题,模拟题)

    The Android University ACM Team Selection Contest Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里 ...

  6. sdut 2163:Identifiers(第二届山东省省赛原题,水题)

    Identifiers Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述  Identifier is an important c ...

  7. sdut 2411:Pixel density(第三届山东省省赛原题,字符串处理)

    Pixel density Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 Pixels per inch (PPI) or pi ...

  8. sdut 2165:Crack Mathmen(第二届山东省省赛原题,数论)

    Crack Mathmen Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述  Since mathmen take securit ...

  9. sdut 2159:Ivan comes again!(第一届山东省省赛原题,STL之set使用)

    Ivan comes again! Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 The Fairy Ivan gave Say ...

随机推荐

  1. 用批处理启动MySQL命令行工具

    最近在看MySQL,安装好之后,每次在开始菜单去启动MySQL命令行工具的时候,都是直接用root用户连接我本地的数据库 输入密码开始工作,但是要连接服务器上的MySQL的话,就要去CMD下运行 : ...

  2. mysql中,主键与普通索引

    一.什么是索引?索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-树的形式保存.如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录.表里 ...

  3. UNIX进程

    UNIX进程控制的博客   http://blog.csdn.net/yang_yulei/article/details/17404021 Linux的概念与体系    http://www.cnb ...

  4. hdu3709

    枚举+数位dp 注意处理数字为0和1的情况. #include <cstdio> #include <cstring> using namespace std; #define ...

  5. Java集合中Set的常见问题及用法

    在这里演示的案例是衔接Java集合中的List(点击查看)那篇博文的,本节我们学习的Set的用法. Set是Collection的一个重要的子接口,Set中的元素是无序排列的,并且元素不可以重复,被称 ...

  6. ios中的RunLoop 和 android 中的Looper

    今天写android程序,用到了Handler,晚上回来查阅资料,发现了Looper这个概念. 看了一下网上关于Looper的资料,发现这个Looper跟ios中的Runloop基本的理念完全一致! ...

  7. perl 二维数组

    perl没有真正的二维数组,所谓的二维数组其实是把一维数组以引用的方式放到另外一个一维数组. 二维数组定义 : my @array1=([1,2],[3,4],[45,9],[66,-5]);     ...

  8. ffmpeg-20160628-git-bin.7z

    ESC 退出 0 进度条开关 1 屏幕原始大小 2 屏幕1/2大小 3 屏幕1/3大小 4 屏幕1/4大小 S 下一帧 [ -2秒 ] +2秒 ; -1秒 ' +1秒 下一个帧 -> -5秒 f ...

  9. nyoj298_点的变换_错误

    点的变换 时间限制:2000 ms  |  内存限制:65535 KB 难度:5   描述 平面上有不超过10000个点,坐标都是已知的,现在可能对所有的点做以下几种操作: 平移一定距离(M),相对X ...

  10. codeforces 501C. Misha and Forest 解题报告

    题目链接:http://codeforces.com/problemset/problem/501/C 题目意思:有 n 个点,编号为 0 - n-1.给出 n 个点的度数(即有多少个点跟它有边相连) ...