本题的数据规模

从别的oj网址上找的:
数据范围: N <= 5000 ,height和speed不大于10000。A、B、C在长整型以内。

吐槽

牛客网的分类简直迷惑,这题竟然归为了堆?而且还是5星?
我往堆上靠了半天没想出来,最后换一种思路一下就做出来了

解题思路

一种很容易想到的思路是:先分别对h,v排序,然后枚举min_h,枚举min_v,枚举所有人,这样是O(n^3),明显不行
那么怎么优化呢?
我们先看看题目的要求,这题的要求简化一下只有三点:
1.a * (h>min_h ) + b * ( v-min_v ) ≤ c
2. h>min_h
3. v>min_v
当我们枚举min_h时,可以发现:
对于每个人(人肯定要枚举的),唯一的变量只有min_v,即对于每个人,会使得[min_v,v[i]]这个区间的人+1,这是简单的区间更新,求单点的值,使用线段树?线段树是O(nlogn),(为什么是nlogn?更新的时候是logn,但是查询的时候我们要遍历所有的存在的v,所以使用线段树的总复杂度是O(n^2logn)别忘了最外层枚举min_h还有一层循环。)
感觉有点不稳,能不能优化呢?
可以!
我们发现,这题涉及的操作只有区间更新和单点查询,那么一个差分+一个前缀和就搞定了,于是复杂度被优化为O(n max_v),此题的v小于等于10000,所以可以满足要求

完整代码

#include<iostream>
#include<algorithm>
#include<cstring> 
#include<cstdio>
using namespace std;
inline long long Read(){
    long long x=0;
    char c=getchar();
    while(c>'9'||c<'0')c=getchar();
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
    return x;
}
const int mx=50001;
int n;
int h[mx];
int is_v[mx*2];//哪些v是存在的 
int cnt_v[mx*2];//cnt_v[x]:min_v<=x的人的个数(本质上是前缀和) 
int d[mx*2];//差分数组 
long long a,b,c;
struct People{
    int h,v;
}p[mx];
int main(){
    n=Read(),a=Read(),b=Read(),c=Read();
    int max_v=0;
    for(int i=1;i<=n;++i){
        p[i].h=h[i]=Read();
        int v;
        p[i].v=v=Read();
        is_v[v]=1;
        max_v=max(max_v,v);
    }
    sort(h+1,h+1+n);
    int ans=0;
    for(int i=1;i<=n;++i){//枚举最小的h 
        for(int j=1;j<=n;++j){//遍历所有人 
            if(p[j].h<h[i]||a*(p[j].h-h[i])>c)continue;
            int min_v=max((long long)0,p[j].v-(c-a*(p[j].h-h[i]))/b);//这个人所需要的最小的min_v
            d[min_v]+=1;//[min_v,p[j].v]范围内的min_v都符合要求 
            d[p[j].v+1]-=1; 
        }
        cnt_v[0]=d[0]; 
        for(int k=1;k<=max_v;++k){
            cnt_v[k]=cnt_v[k-1]+d[k];
        }
        for(int k=0;k<=max_v;++k)if(is_v[k])ans=max(ans,cnt_v[k]);
        for(int k=0;k<=max_v;++k)d[k]=cnt_v[k]=0;
    }
    printf("%d",ans);
    return 0;
}