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