题目链接:点击查看
题目大意:n 个机器人在数轴上赛跑,给出每个机器人的起点和加速度,初始速度都为 0 ,问有多少个机器人在赛跑的过程中可以成为最前面的一个
题目分析:又是被zx学长秒掉的一道题,感谢zx学长的耐心讲解
首先根据高中物理知识,根据已知条件,可以得到位移与时间的方程 ,y 代表位移,x 代表时间,b 代表初始位置,k 代表加速度
因为都是抛物线,求交点非常的麻烦,因为我们只需要求交点的相对位置,所以可以将方程转换为位移与时间的平方的方程:,这样并不会影响交点的相对位置,同时将每一个位移曲线都转换为一条直线表示了
那么如何将机器人成为最前面的一个体现出来呢?将 n 条直线对应到平面直角坐标系中,将 x 轴从 [ 0 , inf ) 划分为无数个点,对于每个 x 而言,对应位置最上面的那条直线所代表的机器人,就是在 x 时刻最领先的机器人,所以我们只需要统计有多少条直线成为过最上面的直线就好了
按照斜率排序,就可以发现相邻的三条直线会出现下面两种情况:
我们将直线 i - 1 与直线 i 的交点记为 ( x1 , y1 ) ,将直线 i 与直线 i + 1 的交点记为 ( x2 , y2 ) ,发现当 x1 < x2 时,直线 i 所代表的机器人才有可能在最上面,而当 x1 >= x2 时,直线 i 完全被直线 i - 1 和直线 i + 1 所挡住,也就无法在最上面了
到此为止,我们就可以利用单调栈来实现上述模拟了,栈内维护的是都可以在最上面的直线,对于新遍历到的直线 i ,因为我们已经按照斜率递增了,显然单调栈内所有初始位置 b 小于当前直线的直线都是不符合条件的,如下图所示:
显然在上图中,st [ top ] 所代表的直线完全被第 i 条直线所覆盖
还有就是对于栈中的元素,将 st[ top - 1 ] , st[ top ] 和 i 这三条直线分别对应上文中的 i - 1 , i , i + 1 三条直线来比较交点的位置亦可以判断合理性
最后有个坑点,就是如果有两个机器人,起点和加速度都是相同的话,那么这两个机器人是不会对答案提供贡献的,这个可以用 map 判断一下
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=5e4+100;
const double eps=1e-8;
const double pi = acos(-1.0);
int sgn(double x){
if(fabs(x) < eps)return 0;
if(x < 0)return -1;
else return 1;
}
struct Line
{
LL k,b;
bool operator<(const Line& t)const
{
if(k!=t.k)
return k<t.k;
return b>t.b;
}
}line[N];
int st[N],tp;
double get_x(int i,int j)//y=k1x+b1,y=k2x+b2,前式减后式,得到x=-1.0*(b1-b2)/(k1-k2),得到x的坐标
{
return (-1.0*(line[i].b-line[j].b))/(1.0*(line[i].k-line[j].k));
}
map<pair<LL,LL>,int>mp;
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int w;
cin>>w;
while(w--)
{
mp.clear();
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&line[i].b,&line[i].k);
line[i].b*=2;//因为只需要求交点的相对位置,所以x和y轴同时放大两倍,避免出现1/2
mp[make_pair(line[i].b,line[i].k)]++;
}
sort(line+1,line+1+n);
tp=0;
for(int i=1;i<=n;i++)
{
if(line[i].k==line[i-1].k)//两直线平行
continue;
while(tp&&line[i].b>=line[st[tp]].b)//如果当前直线在栈顶的直线上面,说明栈顶的直线不可能被看到
tp--;
while(tp>1&&sgn(get_x(st[tp-1],st[tp])-get_x(st[tp],i))>=0)
tp--;
st[++tp]=i;
}
int ans=0;
for(int i=1;i<=tp;i++)
{
if(mp[make_pair(line[st[i]].b,line[st[i]].k)]==1)
ans++;
}
printf("%d\n",ans);
}
return 0;
}