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

题目大意

题目链接

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

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

数据范围:\(1\leq n\leq 50000\)\(0\leq p_i,a_i\leq 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{2\cdot \frac{(p_j-p_i)}{(a_i-a_j)}} \]

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

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

  • 如果\(p_i\geq 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_2\geq t_1\),说明\(j\)永远不可能领先了,将其出栈。

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

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

时间复杂度\(O(n\log 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;
}
posted @ 2020-07-22 15:14  duyiblue  阅读(327)  评论(0编辑  收藏  举报