洛谷——P3184 [USACO16DEC]Counting Haybales数草垛

博客围绕农夫John的干草垛分布问题展开,他在数轴上放置N个干草垛,有Q个询问,每个询问给出范围,求该范围内干草垛数量。介绍了输入输出格式和样例,提到做法多样,作者采用排序加二分法,还提及c++ stl库有简便二分函数。

题目描述

Farmer John has just arranged his NN haybales (1 \leq N \leq 100,0001≤N≤100,000) at various points along the one-dimensional road running across his farm. To make sure they are spaced out appropriately, please help him answer QQ queries (1 \leq Q \leq 100,0001≤Q≤100,000), each asking for the number of haybales within a specific interval along the road.

农夫John在一条穿过他的农场的路上(直线)放了N个干草垛(1<=N<=100,000),每个干草垛都在不同的点上。为了让每个干草垛之间都隔着一定的距离,请你回答农夫John的Q个问题(1<=Q<=100,000),每个问题都会给出一个范围,询问在这个范围内有多少个干草垛。
(其实就是有一条数轴上有N个不重复的点,再问Q个问题,每个问题是给出一个范围,问此范围内有多少个点?)
(在给出范围的边界上也算)

输入输出格式

输入格式:
The first line contains NN and QQ.

The next line contains NN distinct integers, each in the range 0 \ldots 1,000,000,0000…1,000,000,000, indicating that there is a haybale at each of those locations.

Each of the next QQ lines contains two integers AA and BB(0 \leq A \leq B \leq 1,000,000,0000≤A≤B≤1,000,000,000) giving a query for the number of haybales between AA and BB, inclusive.

第一行包括N和Q

第二行有N个数字,每个数字的范围在0~1,000,000,000,表示此位置有一个干草垛。

接下来的Q行,每行包括两个数字,A和B(0<=A<=B<=1,000,000,000)表示每个询问的范围

输出格式:
You should write QQ lines of output. For each query, output the number of haybales in its respective interval.

总共Q行,每行输出此范围内的干草垛数量

样例1:
4 6
3 2 7 5
2 3
2 4
2 5
2 7
4 6
8 10
输出1:
2
2
3
4
1
0

解析:题目看起来很友好,实际上~~~~~~~~,做法很多,本弱鸡数据结构很烂,离线的树状数组做法不会,只会排序+二分,最后搜到c++stl库有两个函数可以简便二分,其本质也是二分的。
代码:

//排序+二分查找 

#include<bits/stdc++.h>
using namespace std;

const int N = 100000 + 10;
//const long long INF = 1e10;

int a[N], n, m;
//a数组记录位置 n为有n个点 m表示m次询问

int left_mid_find(long long x) {//二分查找左边界
    long long kl = 1, kr = n;
    if(x >= a[n]) return n;
    if(x <= a[1]) return 1;
    //特判
    while(kl < kr) {
        int mid = (kl + kr + 1) >> 1;
        if(a[mid] == x) 
		   return mid;             //当前位置等于查找值
        if(a[mid - 1] < x && a[mid] > x) 
		   return mid;            //查找值在当前位置与当前位置-1之间
        if(a[mid + 1] > x && a[mid] < x) 
		  return mid + 1;         //查找值在当前位置与当前位置+1之间
        if(a[mid] > x) kr = mid;
        else kl = mid - 1;
    }
    return kl;
}

int right_mid_find(long long x) {//二分查找右边界
    long long kl = 1, kr = n;
    if(x >= a[n]) return n;
    if(x <= a[1]) return 1;
    //特判
    while(kl < kr) {
        int mid = (kl + kr + 1) >> 1;
        if(a[mid] == x) 
		   return mid;              //同上        
        if(a[mid - 1] < x && a[mid] > x) 
		   return mid - 1;     //注意return值不同
        if(a[mid + 1] > x && a[mid] < x) 
		   return mid;
        if(a[mid] > x) 
		   kr = mid;
        else kl = mid - 1;
    }
    return kl;
}

int main() {
    int k, j, i;
    cin >> n >> m;
    for(i = 1; i <= n; i++) cin >> a[i];//读入
    sort(a + 1, a + n + 1);
    for(i = 1; i <= m; i++) {
        cin >> j >> k;
        if((j < a[1] && k < a[1])||(j > a[n] && k > a[n])) {
            //特判
            cout << 0 << endl;
            continue;
        }
        int l = left_mid_find(j);//左边界位置
        int r = right_mid_find(k);//右边界位置
        cout << r - l + 1<< endl;//等价与r - (l - 1)
    }
    return 0;
}

/*总体思想仍然是排序 + 二分
algorithm库里有两个叫upper_bound和lower_bound的函数,
他们的作用就是二分找比某个数小并与它最接近的那个数的下标和二分找比某个数大并与它最接近的那个数的下标。*/
#include<bits/stdc++.h>
#define maxn 100010
using namespace std;

int n,m,q,tot,x,y;
int p[maxn];

int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)  
      scanf("%d",&p[i]);
    sort(p+1,p+1+n);
    for(int i=1;i<=q;i++){
        scanf("%d%d",&x,&y);
        cout<<upper_bound(p+1,p+n+1,y) - lower_bound(p+1,p+n+1,x)<<endl;//注意:y是后面一个数,所以是先查后面一个数,再相减
    }
}

额,树状数组的代码还是到这吧,万一以后会了呢

//树状数组维护前缀和 
#include<bits/stdc++.h>//万能头文件
using namespace std;
inline int read()
{
    int f=1,num=0;
    char ch=getchar();
    while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(0!=isdigit(ch)) num=(num<<1)+(num<<3)+ch-'0',ch=getchar();
    return num*f;
}//标配快读
const int N=350000;
int l[N],x[100010],top=0;
int tot=0;int a[N],b[N];
int n,m;
int ans[100050][3];//储存答案
int t[N];//树状数组

struct node{
    int f,id,x;
}q[N];

inline int find(int x){
    int L=1,R=top;
    while(L<=R){
        int mid=(L+R)>>1;
        if(l[mid]>=x){R=mid-1;}
        else L=mid+1;
    }
    return L;
}//用二分寻找离散化后的点

bool cmp(node a,node b){
    if(a.x==b.x)return a.f<b.f;
    return a.x<b.x;
}//先考虑所在位置,再优先处理有草垛的点

inline int lowbit(int x){
    return x&-x;
}//树状数组

inline void add(int x)//树状数组加入点
{
    for(register int i=x;i<=tot;i+=lowbit(i)){
        t[i]+=1;//依次向上传递值
    }
}
inline int query(int x)查询编号比x小的点总数
{
    int sum=0;
    for(register int i=x;i;i-=lowbit(i)){
        sum+=t[i];//从编号x向下查询
    }
    return sum;
}
int main(){
    n=read();m=read();
    for(register int i=1;i<=n;i++){
        x[i]=read();//读入草垛坐标
        l[++top]=x[i];压入第一个队列
    }
    for(register int i=1;i<=m;i++){
        a[i]=read();//读入询问区间左端点
        l[++top]=a[i];
        b[i]=read();//右端点
        l[++top]=b[i];
    }
    sort(l+1,l+1+top);//注意先排序
    for(register int i=1;i<=n;i++){
        x[i]=find(x[i]);//离散化
        q[++tot].x=x[i];//压入第二个队列
    }
    for(register int i=1;i<=m;i++){
        a[i]=find(a[i]);b[i]=find(b[i]);//离散化
        q[++tot].x=a[i]-1;q[tot].id=i;q[tot].f=1;
        q[++tot].x=b[i];q[tot].id=i;q[tot].f=2;
        //压入询问区间左端点与右端点、询问区间编号
    }
    sort(q+1,q+1+tot,cmp);//再排一次序
    for(register int i=1;i<=tot;i++){
        if(!q[i].f)add(i);//如果是草垛,加点
        else{
            ans[q[i].id][q[i].f]=query(i);//计算前缀和
        }
    }
    for(register int i=1;i<=m;i++){
        cout<<ans[i][2]-ans[i][1]<<endl;//输出答案
    }
    return 0;
}

/**
*  ┏┓   ┏┓+ +
* ┏┛┻━━━┛┻┓ + +
* ┃       ┃
* ┃   ━   ┃ ++ + + +
*  ████━████+
*  ◥██◤ ◥██◤ +
* ┃   ┻   ┃
* ┃       ┃ + +
* ┗━┓   ┏━┛
*   ┃   ┃ + + + +Code is far away from  
*   ┃   ┃ + bug with the animal protecting
*   ┃    ┗━━━┓ 神兽保佑,代码无bug 
*   ┃        ┣┓
*    ┃        ┏┛
*     ┗┓┓┏━┳┓┏┛ + + + +
*    ┃┫┫ ┃┫┫
*    ┗┻┛ ┗┻┛+ + + +
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值