2020多校1 - Leading Robots(单调栈)
题意:有 T T T 个赛事,每个赛事中有 N N N 个机器人准备在从左往右水平赛道上赛跑,已知每个机器人的初始位置 p i p_i pi 以及加速度 a i a_i ai,初始速度为 0 0 0,现假设赛道无限长的情况下有多少个机器人会在赛跑过程中处于过“领先”情况,某个机器人“领先”定义为该机器人在当前时刻 t t t 跑在所有人前面且是唯一一个。
范围: 1 ≤ T ≤ 50 ; 0 < n ≤ 5 e 4 ; 0 < p i , a i < 2 31 1 \le T \le 50;0 < n \le 5e4;0 < p_i, a_i < 2^{31} 1≤T≤50;0<n≤5e4;0<pi,ai<231
分析: 我是这样思考的,由于初始位置以及加速度都是固定的,那么如果当前的领跑者 A A A 被某个机器人 B B B 超过了,那么之后 A A A 永远都不可能再追上 B B B,因此我们只需要不断确定当前的领跑者会不会接下来被其他机器人追上。
暴力的方法是先根据机器人的初始位置升序以及加速度升序进行排序,那么排序后最后一个机器人就是当前的第一个领跑者,然后我们可以每轮对其后方的机器人求最小的相遇时间 t = m i n ( 2 ∗ ( p j − p i ) a i − a j ) t = min(\frac{2*(p_j-p_i)}{a_i-a_j}) t=min(ai−aj2∗(pj−pi)),如果 t t t 的值合法,那么就让 j j j 成为新的领跑者,计数器 + 1 +1 +1,重复上述过程直至 t t t 不合法。时间复杂度为 O ( n 2 ) O(n^2) O(n2) ,显然是不行的。
另外我们需要考虑唯一性的问题,可以预处理出哪些机器人是重叠的,在计数的时候进行判断即可。
上述的暴力方法存在很多重复的计算,可以发现每次领跑者被超越的时间 t t t 总是单调递增的,考虑使用单调栈来进行优化。
根据上面的路程-时间图像,线条已经进行了排序( a 3 a_3 a3 > a 2 a_2 a2),若没有线条 4 4 4,那么 1 , 2 , 3 1,2,3 1,2,3 轮流成为领跑者。现在出现了 4 4 4,先考虑线条 2 , 3 , 4 2,3,4 2,3,4,由于 4 4 4 与 2 2 2 相遇的时间早于 3 3 3 与 2 2 2 的相遇时间,那么 3 3 3 无论如何都无法领跑,剩下再考虑线条 1 , 2 , 4 1,2,4 1,2,4,由于 4 4 4 与 1 1 1 的相遇时间早于 2 2 2 与 1 1 1 的相遇时间,那么 2 2 2 无论如何都无法领跑,这样的过程与单调栈的出栈入栈操作是一致的。
具体方式:
若栈大小 < 3 < 3 <3,直接将线条压栈。
否则取出栈顶三个元素 a , b , c a, b, c a,b,c( c c c 在底部),
若 a a a 与 c c c 的相遇时间小于等于 b b b 与 c c c 的相遇时间,那么将 b b b 出栈,重复上述过程(此时说明 a a a 会早于 b b b 遇到 c c c, b b b 由于加速度劣势永远追不上,成为不了领跑人);
否则结束操作(此时满足领跑时刻单调递增)。
Code:
#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
const int MAXN = 5e4 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
int n;
struct Node
{
int p, a;
bool operator<(Node other) const
{
if (p == other.p)
return a < other.a;
else
return p < other.p;
}
} nodes[MAXN];
stack<int> st;
int vis[MAXN];
signed main()
{
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int T = read();
while (T--)
{
memset(vis, 0, sizeof(vis));
while (!st.empty())
st.pop();
n = read();
for (int i = 0; i < n; i++)
{
nodes[i].p = read(), nodes[i].a = read();
}
sort(nodes, nodes + n);
// 预处理重叠的机器人
for (int i = 1; i < n; i++)
{
if (nodes[i].p == nodes[i - 1].p && nodes[i].a == nodes[i - 1].a)
{
vis[i] = vis[i - 1] = 1;
}
}
for (int i = n - 1; i >= 0; i--)
{
// 先入栈一个
if (i == n - 1)
{
st.push(i);
}
else
{
int pre = st.top();
// 若位置相同,或者加速度较小,绝对无法追上
if (nodes[i].p == nodes[pre].p || nodes[i].a <= nodes[pre].a)
{
continue;
}
// 栈中至少要有两个元素
if (st.size() == 1)
{
st.push(i);
}
else
{
while (st.size() > 1)
{
st.pop();
int prepre = st.top();
st.push(pre);
// 计算出两个相遇时间
double t1 = 2.0 * (nodes[prepre].p - nodes[pre].p) / (nodes[pre].a - nodes[prepre].a);
double t2 = 2.0 * (nodes[pre].p - nodes[i].p) / (nodes[i].a - nodes[pre].a);
// 满足单调,退出
if (t1 < t2)
{
break;
}
else
{
// 栈顶机器人永远追不上,丢弃
st.pop();
}
pre = st.top();
}
// 找到位置安心放下
st.push(i);
}
}
}
// 统计唯一的领跑人
int ans = 0;
while (!st.empty())
{
int x = st.top();
st.pop();
if (!vis[x])
ans++;
}
cout << ans << endl;
}
return 0;
}
【END】感谢观看