luogu1315 观光公交
题目大意
一个公交车在一条线上从1站、2站走到n站,站站间有距离。另有m个乘客在时间点t在a站等候要到b站。对于一个站,如果车比人早到,则车等人,如果人比车早到,则人等车(我一开始做题时把这个情况当作已知不可能发生了55555)。另外有D个空间瞬移器,使得汽车在0秒内前进一个单位距离。站与站间可重复用空间瞬移器,但用两站间使用空间瞬移器的数量不多于两站间距离。现在要求对空间瞬移器进行最优安排,使得所有乘客等车+在车上的时间和最小,输出这个最小值。
思路
不使用加速器?
如果不使用加速器,那么汽车的时间安排肯定是在每个站点接到最后一个乘客后直接出发,否则浪费那时间干什么呢?
特殊情况?
我们的入手点在于:在当前的形式下(用了加速器相当于两站点间的距离减少一,变成了同一个问题),我们要把当前的加速器用到哪一段路上。不要只想一个情况!以下将在一个站点最后一个开始等候的人简称为“人”。
- 在不使用加速器的情况下,所有站点都是车等人:如果在a站到b站间使用加速器,只有在b站下车的人被影响。因此我们在与前一站仍有距离的站中选择在此站下车的人最多的站的之前的路段使用加速器。
- 在不使用加速器的情况下,所有站点都是人等车:我们要在最一开始的路段上使用加速器,因为这样整条路上所有人的时间都会减少1。
加上另一种情况?
1没有什么思路;但2有。如果站a、b为两个车等人的站,那么如果在站a和a+1之间使用加速器,则在[a+1,b]站下车的人的时间都会减少1!
因此我们根据当前车等人的站将整个路段拆分成几个区间,我们每次都选出下车人最多的区间(统计下车人可以使用前缀和)(动态选出最多可以使用优先队列)。将其在一开始加速(意味着区间内所有站点车的到达时间(出发时间(人等车时,出发时间和))都-1)后,将该区间由人等车变为人车同时到的站点拆分成子区间再次加入队列,直到加速器用完为止。
具体实现细节
下定义
提前把一些定义下好了,可以节省大量时间。如:
- 能加速的区间:若在站点Range.L前能够使用加速器(与前一个站点有距离),则在站点[Range.L, Range.R]下车的人时间都会减少1。这些区间可放入优先队列。
- Range::GetAccableSubranges:不管Range是个什么区间,提取出其能被加速的子区间。
- 上述函数中的l,r:保证[l,r]包含于一个能加速的区间内。
整个路线的起点、终点如何处理?
特殊的是终点,因为其肯定没有人等车了。那么此时“最后一个人在此站点等候的时间”必须要设为无穷大,否则其被认为是有一个人在0时间在最后一个站点等候,这就错了。
GetAccableSubranges时,如何处理两个站点间距离为0的情况?
明确一点:距离为0的影响在于,对于一个全部都是人等车的区间,如果该区间的左端点之前的距离为0,则该区间不能被加速。(如果一个prevdist==0的站点之前是人等车的站,此时照常处理,所以不要在加速时assert(区间中当前节点的prevdist>0))而只有当左端点为初始态或被更改时(得到一个子区间[l,r],然后l=r+1)才有可能遇到上述情况,而此时r必然等于l。因此如果区间l的prevdist==0,则l跳过(l++),r随之更改(r++)。
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <vector> #include <cassert> using namespace std; const int MAX_PASS = 10010, MAX_STAT = 1010, INF = 0x3f3f3f3f; int TotStat, TotPass, TotAcc; int OffPassPrefix[MAX_STAT]; struct Stat { int InTime, OutTime; int OffPassCnt, LastPassTime; int PrevDist; }_stats[MAX_STAT]; struct Pass { Stat *S, *T; int Time; Pass(int s, int t, int time):S(_stats+s),T(_stats+t),Time(time){} Pass(){} }_passes[MAX_PASS]; struct Range { int OffPassCnt; int L, R; Range(int l, int r) :L(l), R(r), OffPassCnt(OffPassPrefix[r]-OffPassPrefix[l - 1]){} bool operator < (const Range& a) const { return OffPassCnt < a.OffPassCnt; } void GetAccableSubranges(void(*func) (Range)) { int tl = L; for (int r = L; r <= R; r++) { if (_stats[tl].PrevDist == 0) { assert(tl == r); tl++; } else if (_stats[r].InTime <= _stats[r].LastPassTime) { func(Range(tl, r)); tl = r + 1; } } } void Accelerate()//Accelerate at Left Prev. { for (int i = L; i <= R - 1; i++) { assert(_stats[i].InTime == _stats[i].OutTime); //assert(_stats[i].PrevDist);//Don't assert! _stats[i].InTime--; _stats[i].OutTime--; } _stats[R].InTime--; _stats[L].PrevDist--; TotAcc--; } }; void Read() { scanf("%d%d%d", &TotStat, &TotPass, &TotAcc); for (int i = 2; i <= TotStat; i++) scanf("%d", &_stats[i].PrevDist); for (int i = 1; i <= TotPass; i++) { int s, t, sTime; scanf("%d%d%d", &sTime, &s, &t); _passes[i] = Pass(s, t, sTime); } } void Init() { for (int i = 1; i <= TotPass; i++) { _passes[i].S->OutTime = max(_passes[i].S->OutTime, _passes[i].Time); _passes[i].T->OffPassCnt++; _passes[i].S->LastPassTime = max(_passes[i].S->LastPassTime, _passes[i].Time); } for (int i = 2; i <= TotStat; i++) { _stats[i].InTime = _stats[i - 1].OutTime + _stats[i].PrevDist; _stats[i].OutTime = max(_stats[i].OutTime, _stats[i].InTime); } _stats[TotStat].LastPassTime = INF; for (int i = 1; i <= TotStat; i++) OffPassPrefix[i] = _stats[i].OffPassCnt + OffPassPrefix[i - 1]; } priority_queue<Range> q; void InQ(Range r) { q.push(r); } void DoAcc() { Range(1, TotStat).GetAccableSubranges(InQ); while (!q.empty() && TotAcc) { Range cur = q.top(); q.pop(); cur.Accelerate(); cur.GetAccableSubranges(InQ); } } int GetAns() { int ans = 0; for (int i = 1; i <= TotPass; i++) ans += _passes[i].T->InTime - _passes[i].Time; return ans; } int main() { Read(); Init(); DoAcc(); printf("%d\n", GetAns()); return 0; }