题意
n
n
个景点, 个游客,第
i
i
个游客在 时间到达
Ai
A
i
景点,要去
Bi
B
i
景点。有一个公交车在
0
0
时刻在 号景点出现,从
i
i
号景点到 号景点需要
Di
D
i
时间,从第
i
i
个景点去往第 个景点必须保证所有出现在
Ai
A
i
的人均上车(上下车不需时间)。现在有
k
k
个加速器,每个加速器可以使得某一个 减
1
1
,但不能小于 ,求所有游客从刚到景点到下车时间差值的和最小是多少。
1≤n≤1000
1
≤
n
≤
1000
1≤m≤10000
1
≤
m
≤
10000
思路
题目比较复杂,但所求还是比较清晰的,因为游客刚到景点的时间已经给定,所以只用求所有游客下车时间总和的最小值即可。我们单独分析在每个点使用加速器的效果,不难发现,当公交车不用等来的晚的游客时,这个效果可以抑制持续下去,直到遇到了一个来的和公交车一样晚,甚至更晚的游客,在这之后下车的游客便不受影响了。这样我们也不难发现一个贪心决策,先处理处一下连续影响的区间,按照下车人数从大到小排序,每弹出一个 [l,r] [ l , r ] 决策是在 l−1 l − 1 到 l l 的路上用加速器,直到某一个点的人来的时间和公交车一样了,并把这个区间沿着这个点劈开;而当 被加速成 0 0 时还没有遇到劈开,那就把 加入堆。
代码
#include<bits/stdc++.h>
#define FOR(i,x,y) for(register int i=(x);i<=(y);++i)
#define DOR(i,x,y) for(register int i=(x);i>=(y);--i)
#define N 1003
#define M 10003
typedef long long LL;
using namespace std; //S[i]表示目前公交车刚到i的时间
int onbus[N],cnt[N],D[N],S[N],T[M],A[M],B[M];
int n,m,K;
struct node
{
int l,r; //使[l,r]中的乘客时间减少1
bool operator <(const node &_)const{return cnt[r]-cnt[l-1]<cnt[_.r]-cnt[_.l-1];}
};
priority_queue<node>q;
int main()
{
scanf("%d%d%d",&n,&m,&K);
FOR(i,1,n-1)scanf("%d",&D[i]);
FOR(i,1,m)
{
scanf("%d%d%d",&T[i],&A[i],&B[i]);
onbus[A[i]]=max(onbus[A[i]],T[i]);
cnt[B[i]]++;
}
FOR(i,2,n)cnt[i]+=cnt[i-1];
FOR(i,2,n)S[i]=max(S[i-1],onbus[i-1])+D[i-1];
FOR(i,2,n)
if(S[i]>onbus[i])
{
FOR(j,i,n)
if(j==n||S[j]<=onbus[j])
{
q.push((node){i,j});
i=j;
break;
}
}
while(!q.empty()&&K>0)
{
node now=q.top();q.pop();
int miner=min(K,D[now.l-1]),p=-1;
FOR(i,now.l,now.r-1)
if(S[i]-onbus[i]<miner)
{
miner=S[i]-onbus[i];
p=i;
}
D[now.l-1]-=miner,K-=miner;
FOR(i,now.l,now.r)S[i]=max(S[i-1],onbus[i-1])+D[i-1];
if(~p)q.push((node){now.l,p}),q.push((node){p+1,now.r});
else if(now.l<now.r)q.push((node){now.l+1,now.r}); //无法继续加速
}
int ans=0;
FOR(i,1,m)ans+=S[B[i]]-T[i];
printf("%d\n",ans);
return 0;
}