本题的数据规模
从别的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;
} 


京公网安备 11010502036488号