首先可得到这个式子:
(frac{A}{a_{i}}+frac{B}{b_{i}}=T)
其中 (T) 表示时间,这个式子应该不难理解,题目就转换成了对于一对 ((a_{i}) , (b_{i})) ,如果存在一对 ((A,B)) 使得时间 (T) 在 ((a_{i}) , (b_{i})) 上最小,就输出。
接下来是做法:
考虑把 ((frac{1}{a_{i}}) , (frac{1}{b_{i}})) 看做平面上的点,原式变成 (Ax+By=T) ,这就是你们喜欢的一次函数啦。继续把原式变得更通俗
(y=-frac{A}{B}x+frac{T}{B})
由于点 ((x,y)) 是给定的,假设你已经确定直线的斜率,也就是 (-frac{A}{B}) ,你就可以算出时间 (T) ,进而轻松地确定哪对点是答案。
那么,怎么确定斜率呢?我们可以通过画图发现,是答案的点一定在平面图的左下凸包上,如果明白凸包的思想,
那么问题就很简单了,至于没学过凸包的同学,可以参考yyb的博客或这篇博客(没学凸包还想做此题?),蒟蒻我就不多说了qwq
如果看懂了上述内容但不懂凸包过程,请转
上代码:
#include<bits/stdc++.h>
#define ll long long
#define ld long double
using namespace std;
inline ll read(){
ll f=1,w=0;char x=0;
while(!(x>='0'&&x<='9'))
{
x=getchar();
if(x=='-') f=-1;
}
while(x>='0'&&x<='9')
{
w=(w<<3)+(w<<1)+(x^48);
x=getchar();
}
return f*w;
}//习惯性的快读
struct people{
ll a,b,id;
}p[300010];
ll n,s[300010],t;
bool v[300010],ans[300010];
bool cmp(people a,people b){
if(a.a==b.a) return a.b>b.b;
return a.a>b.a;
}//凸包排序
ld check(people a,people b){
return (ld)a.a*b.a*(b.b-a.b)/(b.a-a.a)/b.b/a.b;
}//斜率的计算
int main(){
n=read();
for(int i=1;i<=n;i++) p[i].a=read(),p[i].b=read(),p[i].id=i;
sort(p+1,p+n+1,cmp);
s[1]=1,t=1;
ll maxb=p[1].b;
for(int i=2;i<=n;i++)
if(p[i].b<=maxb) v[i]=1;
else maxb=p[i].b;
//去重和排除能一眼看出非答案的点,譬如a和b都比另一个点小
for(int i=2;i<=n;i++)
{
if(v[i]||check(p[i],p[s[t]])>0) continue;//斜率大于0排除
while(t>1&&check(p[i],p[s[t]])<check(p[s[t-1]],p[s[t]]))
t--;
s[++t]=i;
}//单调栈维护的类似于凸包的过程
for(int i=1;i<=t;i++)
{
ans[p[s[i]].id]=1;
for(int j=s[i]+1;j<=n&&p[s[i]].a==p[j].a&&p[s[i]].b==p[j].b;j++)
ans[p[j].id]=1;
}//还原位置相同的点
for(int i=1;i<=n;i++)
if(ans[i]) cout<<i<<" ";
return 0;
}