noip2011 公交观光

时间:2021-10-14 19:31:53

描述

风景迷人的小城Y市,拥有n个美丽的景点。由于慕名而来的游客越来越多,Y市特意安排了一辆观光公交车,为游客提供更便捷的交通服务。观光公交车在第0分钟出现在1号景点,随后依次前往2、3、4……n号景点。从第i号景点开到第i+1号景点需要Di分钟。任意时刻,公交车只能往前开,或在景点处等待。

设共有m个游客,每位游客需要乘车1次从一个景点到达另一个景点,第i位游客在Ti分钟来到景点Ai,希望乘车前往景点Bi(Ai<Bi)。为了使所有乘客都能顺利到达目的地,公交车在每站都必须等待需要从该景点出发的所有乘客都上车后才能出发开往下一景点。假设乘客上下车不需要时间。

一个乘客的旅行时间,等于他到达目的地的时刻减去他来到出发地的时刻。因为只有一辆观光车,有时候还要停下来等其他乘客,乘客们纷纷抱怨旅行时间太长了。于是聪明的司机ZZ给公交车安装了k个氮气加速器,每使用一个加速器,可以使其中一个Di减1。对于同一个Di可以重复使用加速器,但是必须保证使用后Di大于等于0。

那么ZZ该如何安排使用加速器,才能使所有乘客的旅行时间总和最小?

格式

输入格式

第1行是3个整数n, m, k,每两个整数之间用一个空格隔开。分别表示景点数、乘客数和氮气加速器个数。

第2行是n-1个整数,每两个整数之间用一个空格隔开,第i个数表示从第i个景点开往第i+1个景点所需要的时间,即Di。

第3行至m+2行每行3个整数Ti, Ai, Bi,每两个整数之间用一个空格隔开。第i+2行表示第i位乘客来到出发景点的时刻,出发的景点编号和到达的景点编号。

输出格式

共一行,包含一个整数,表示最小的总旅行时间。

数据范围

样例说明:

对D2使用2个加速器,从2号景点到3号景点时间变为2分钟。

公交车在第1分钟从1号景点出发,第2分钟到达2号景点,第5分钟从2号景点出发,第7分钟到达3号景点。

第1个旅客旅行时间7 - 0 = 7分钟;
第2个旅客旅行时间2 - 1 = 1分钟;
第3个旅客旅行时间7 - 5 = 2分钟。

总时间7 + 1 + 2 = 10分钟。

数据范围:

对于10%的数据,k = 0;
对于20%的数据,k = 1;
对于40%的数据,2 ≤ n ≤ 50,1 ≤ m ≤ 1,000,0 ≤ k ≤ 20,0 ≤ Di ≤ 10,0 ≤ Ti ≤ 500;
对于60%的数据,1 ≤ n ≤ 100,1 ≤ m ≤ 1,000,0 ≤ k ≤ 100,0 ≤ Di ≤ 100,0 ≤ Ti ≤ 10,000;
对于100%的数据,1 ≤ n ≤ 1,000,1 ≤ m ≤ 10,000,0 ≤ k ≤ 100,000,0 ≤ Di ≤ 100,0 ≤ Ti ≤ 100,000。

----------------------------------------------------------------

正解=贪心

设 time[i] 为到达第 i 的时间,

设 last[i] 为出现在第 i 点的最后一个游客的时间,

题目求所有乘客等待时间和,即ans=(time[b[i]]-T[i])(1<=i<=m)

T[i]为定值,那应使time的值尽量小。

显然time[i]=max(time[i-1],last[i-1])+d[i-1];

由于使用一个加速器后可能会是多个time减少,

last[i]则就成为了一个限制条件(使time[i]的减少无意义),

在一条路径使用加速器后会对后面多个time(不是所有)产生影响,

设在 i 放一个加速器可影响到的点数的数量为f[i],

设 sum 为记录在 i 之前及 i 已下车的人数,

则如果在i点使用加速器后,可影响的人数为sum[f[i]+i]-sum[i],

也就是在 i 到 f[i]+i 之间要到达下一个点的人数

便可以此贪心,

每使用一次加速器后(减少d),重新再算 time 和 f,在从中选出sum[f[i]+i]-sum[i]中最大的,在 i 用加速器

Ps. 100000*1000的复杂度居然能过- =

代码如下:

 #include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#define INF 999999999999
#define LL long long
#define Max(x,y) if(y>x) x=y;
#define N 10010
//using namespace std ;
int f[N],sum[N],dis[N],up[N],down[N],b[N],last[N],time[N];
int n,m,k,total,ans;
int max(int a,int c){
return a > c ? a : c;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=;i< n;i++) scanf("%d",&dis[i]);
for(int i=;i<=m;i++) {
int a,c;
scanf("%d%d%d",&c,&a,&b[i]);
total+=c;
++up[a];
++down[b[i]];
Max(last[a],c);
for(int j=b[i];j<=n;j++) sum[j]++;
}
while(k--){
for(int i=;i<=n;i++)
time[i]=max(time[i-],last[i-])+dis[i-];
f[n]=;
for(int i=n-;i;i--)
if(time[i+]>last[i+])
f[i]=f[i+]+;
else f[i]=; int maxval=-,maxpos=; for(int i=;i<n;i++)
if(dis[i]&&f[i]&&sum[f[i]+i]-sum[i]>maxval){
maxval=sum[f[i]+i]-sum[i];
maxpos=i;
}
if(maxpos) --dis[maxpos];
}
for(int i=;i<=n;i++)
time[i]=max(time[i-],last[i-])+dis[i-];
for(int i=;i<=m;i++) ans+=time[b[i]];
printf("%d",ans-total);
}