【2020杭电多校round1 1009】HDU6759 Leading Robots

题目大意

题目链接

(n)个机器人,每个机器人有一个初始位置(p_i)和加速度(a_i)。在(t)时刻,该机器人的速度是(v_i(t)a_icdot t)

现在所有机器人同时启动,请问无限长的时间之内,会有多少个机器人,至少在一个时刻“领先”?我们说一个机器人领先,是指在这个时刻,它的位置,严格大于其他所有机器人。

数据范围:(1leq nleq 50000)(0leq p_i,a_ileq 2^{31})

本题题解

物理学上有这样一个公式:

[s=p+frac{1}{2}at^2 ]

其中(p)是初始位置,(a)是加速度,(t)是时间,(s)是路程(也就是在(t)时刻的位置)。

利用这个公式,我们可以计算出,两个机器人(i,j)(假设(a_i>a_j), (p_i<p_j)),(i)何时会追上(j)。这相当于解这样一个关于(t)的方程:

[p_i+frac{1}{2}a_it^2=p_j+frac{1}{2}a_jt^2 ]

可以解得:

[t=sqrt{2cdot frac{(p_j-p_i)}{(a_i-a_j)}} ]

回到本题。先将这些机器人,按加速度从小到大排序(加速度相同时按初始位置从小到大排)。然后依次考虑每个机器人,维护一个单调栈,栈里的机器人加速度递增,每一个机器人会在某一时刻超过前一个机器人,并成为领先者(因此他们的初始位置一定是递减的)。

对于当前机器人,栈里的其他机器人,加速度一定小于等于它。拿当前机器人(i)和栈顶的机器人(j)比较((a_igeq a_j)):

  • 如果(p_igeq p_j),则(j)永远也无法领先了,将其出栈。
  • 否则,如果(p_i<p_j)(此时一定有(a_i>a_j),因为(a)相同的机器人是按位置从小到大排序的),那(i)一定会在某个时刻追上(j)。这个时刻可以用我们上述的方程解出来,不妨记为(t_1)。此时,如果栈里有(geq 2)元素,考虑(j)前面的那个机器人(k),计算出(j)追上(k)的时刻,不妨记为(t_2)。如果(t_2geq t_1),说明(j)永远不可能领先了,将其出栈。

如此重复,直到栈顶的元素(j),不满足上述两条,说明我们已经排除了所有“在(i)加入后,不可能再领先的机器人”。此时再将(i)入栈即可。

值得一提的是,判断(t_2geq t_1)时,不需要真的开根号、解出来。只需要移项,转化为乘法。这样可以避免精度误差。

时间复杂度(O(nlog n))

后记:我们可以进一步思考一下这种做法的本质。其实是把(t^2)当成(x),把(s)当成(y),这样一个二次函数,就被转化为了一次函数。而一次函数上的这种追及问题,是很经典的:bzoj1007 水平可见的直线

参考代码:

//problem:hdu6759
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

template<typename T>inline void ckmax(T& x,T y){x=(y>x?y:x);}
template<typename T>inline void ckmin(T& x,T y){x=(y<x?y:x);}

const int MAXN=5e4;
int n;
struct Robot_t{
	ll p,a;
}p[MAXN+5];
bool cmp(Robot_t lhs,Robot_t rhs){
	//按加速度从小到大排,加速度相同按位置从小到大排
	if(lhs.a == rhs.a)
		return lhs.p < rhs.p;
	return lhs.a < rhs.a;
}
bool check(const Robot_t& a,const Robot_t& b,const Robot_t& c){
	assert(a.a < b.a && b.a < c.a);
	// return b追上a的时间 >= c追上b的时间
	return (b.p-a.p) * (b.a-c.a) - (c.p-b.p) * (a.a-b.a) >= 0;
}
int sta[MAXN+5],top;
void solve_case(){
	cin>>n;
	map<pair<ll,ll>,int>mp;
	for(int i=1;i<=n;++i){
		cin>>p[i].p>>p[i].a;
		mp[mk(p[i].p,p[i].a)]++;
	}
	sort(p+1,p+n+1,cmp);
	top=0;
	for(int i=1;i<=n;++i){
		while(top){
			if(p[sta[top]].p <= p[i].p)
				--top;
			else if(top>1 && check(p[sta[top-1]],p[sta[top]],p[i]))
				--top;
			else break;
		}
		sta[++top] = i;
	}
	int ans=top;
	for(int i=1;i<=top;++i){
		if(mp[mk(p[sta[i]].p,p[sta[i]].a)] > 1)
			ans--;
	}
	cout<<ans<<endl;
}
int main() {
	int T;cin>>T;while(T--){
		solve_case();
	}
	return 0;
}
原文地址:https://www.cnblogs.com/dysyn1314/p/13360880.html