Leading Robots
Leading Robots || 单调栈
题意:
一群机器人,已知它们的初始位置和加速度(初速度都为0),同时开始向右运动,求在无限长的时间内,有多少个机器人可以位于最前面(与别人同时为第一不算第一)。有可能有相同起始位置和加速度的机器人。
思路:
我们假设有一个机器人位置序列A、B、C......(A在B前,B在C前),每个机器人的加速度都大于它前面的,保证能追得上。有两个栈,一个存追上的时间,一个存机器人。初始状态,把0push进时间栈,Apush进机器人栈。若B的加速度大于A,那么设tAB为追上的时间,tAB大于时间栈的栈顶元素0,那么把它入栈,同时把Bpush进机器人栈。若B的加速度小于A,那么肯定追不上,也就是说,B没有成为第一的可能了,就无需再考虑B了,那么接着考虑C(这里,我们忽略了加速度和位置相等的一系列需要特殊处理的情况,这些之后再说明)。假设tBC也大于时间栈的栈顶元素tAB,那么把tBC入时间栈,C入机器人栈。重复这个过程,假设一直到机器人E都满足这样的情况,即相邻的追赶时间一直递增,那么就一直push。为什么要push呢?因为tAB < tBC说明在C在追上B之前,B已经追上A了,说明B有可能成为第一。而若tAB > tBC,则说明在C追上B之前,B一直都没追上A,那么B就不可能当第一了。那么我们当然就要pop了!继续刚才的叙述,现在A-E已经顺利入栈,到了F,发现tDE > tEF,即在E追上D之前,F已经追上了E,那么E就不可能当第一了,我们就把Epop出去。当然,tDE也就要跟着pop出去了。这时,就相当于我们不再考虑E这个机器人了。现在栈顶是D和tCD。这时我们计算tDF。如果tCD > tDF,即在D追上C之前F已经追上D,那么D就不可能当第一了,我们就把Dpop出去,tCD也跟着pop出去。就这样,一路计算,一路pop,直至到某个栈顶元素i,我们就把Fpush进去,tiF也跟着push进去。这样,我们就形成了《单调栈》。时间栈是单调递增的,保证了相应的机器人栈里的元素都能当第一(最后还要排除与别人同时当第一的情况)。
于是,我们先把机器人按初始位置排序。位置在后面,加速度还比前面小的机器人不可能当第一,所以我们以加速度为第二关键字排序。排序后,依次遍历,我们将筛选出一部分机器人(将一些无需再判断的剔除,选出的一个序列表示后一个机器人可能超过前一个机器人),然后对筛选出的这部分机器人进行单调栈。遍历的时候,如果当前的机器人初始位置和它前一个不同(不同一定就是小于),需要判断当前的机器人的加速度是否大于筛选出的最后一个机器人的(不一定大于,因为加速度是第二关键字!!!)。如果大于,说明当前机器人有可能超越前一个,那么把它选入。如果小于或等于,说明它不可能追上前面那个,自然也不可能当第一,那么就无需考虑它。如果当前的机器人初始位置和它前一个相同,继续判断加速度。如果加速度也相同,那么把筛选出的最后一个元素的flag置为1, 表示即使它在某一时刻当了第一,也不能算,因为有和它初始位置和加速度完全一样的机器人。为什么是把筛选出的最后一个元素的flag置为1呢?因为最后一个元素必为和当前机器人起始位置和加速度完全一样的。起始位置和加速度完全一样的,只需选入一个即可,而且flag置为1。如果加速度不一样,那必定是小于,也就是说两者起始位置相同,加速度必前者小,那么它永远不可能当第一,也就不用考虑了。
这样得到了一组新序列,这个序列中机器人加速度各不相同,后者大于前者,起始位置也各不相同,后者大于前者。然后按之前的想法构造单调栈。最后遍历一遍单调栈,将flag == 1的剔除,就得到了ans。
bug:
1)时间栈和u没开double;
2)双关键字排序,第二关键字不一定有序,所以加速度不一定是降序的,if条件漏写该判断;
3)结构体赋值可以整体赋值,而开始没有整体赋值,flag一直忘记赋过去了;
4)计算追赶时间的时候正负写反了;
5)栈的上届其实只需一个,然后刚开始遍历的时候上届还写错了;
6)按初始位置排序,p大的在前面,开始写反了;
#include <bits/stdc++.h> using namespace std; const int maxn = 5e4 + 7; struct robot { int p, a; bool flag; }rb[maxn], rb2[maxn]; bool cmp(robot x, robot y) { if(x.p != y.p) return x.p > y.p; return x.a > y.a; } double t[maxn]; robot r[maxn]; int main() { int q; scanf("%d", &q); while(q--) { int n; scanf("%d", &n); for(int i = 1; i <= n; ++i) { scanf("%d %d", &rb[i].p, &rb[i].a); rb[i].flag = 0; } sort(rb + 1, rb + n + 1, cmp); int cnt = 0; for(int i = 1; i <= n; ++i) { if(i == 1) { rb2[++cnt] = rb[i]; continue; } if(rb[i-1].p != rb[i].p) { if(rb2[cnt].a >= rb[i].a) continue; rb2[++cnt] = rb[i]; continue; } if(rb[i-1].a == rb[i].a) { rb2[cnt].flag = 1; continue; } } int ptm = 0, pr = 0, cur = 2; t[++ptm] = 0, r[++pr] = rb2[1]; while(cur <= cnt) { double u = 2.0 * (r[pr].p - rb2[cur].p) / (rb2[cur].a - r[pr].a); //cout << u << endl; while(u <= t[ptm]) { --ptm; --pr; u = 2.0 * (r[pr].p - rb2[cur].p) / (rb2[cur].a - r[pr].a); } r[++pr] = rb2[cur]; t[++ptm] = u; ++cur; } int ans = 0; for(int i = 1; i <= pr; ++i) if(!r[i].flag) ++ans; printf("%d\n", ans); } }