题解:
做这题的时候为了敢速度- - 直接orz了神小黑的题解
其实我还是有想一个拙计的方法的- -
dp:f[i][j] 表示到i点使用j个加速器 在i前上车的人的时间和
轻松愉悦转移之 - - 但是有很严重的两个问题
1.空间复杂度O(nk)爆掉
2.时间复杂度O(nk^2)更呵呵- -
于是弃疗
正解:
贪心!
time[i]表示到i点的时间
last[i]表示从i出发的人的最晚到达的时间
sum[i]表示在i或i前下车的人数
f[i]表示在i后time[j]<=last[j]的最小j
贪心每个加速器在哪使用
选出使sum[f[i]]-sum[i]最大的i
证明:
显然每次使用加速器都要在能让最多人加速的地方使用
而如果在i点使用加速器 我能使在i+1到f[i]下车的人都加速
超过f[i]下车的由于要在f[i]车站等人 所以相当于没加速
sum[f[i]]-sum[i] 即为在i+1到f[i]下车的人
优化:
orz神ak想出的优化
我们在选出i的同时 显然可以同时使用好几个加速器 只要使用后i+1到f[i]-1没有 time[j]<last[j]
于是我们可以统计i+1到f[i]-1中last[j]-time[j]的最小值 直接使用这么多
注意 如果这个最小值大于剩余的加速器 或大于i到i+1所需的时间 那么取他们的最小值
原来的时间复杂度为O(nk) 而这样做复杂度为O(n^2)
因为对于任意一对(i,f[i])如果他们被加速过 那么之后就不可能再加速了
代码:
1 #include <cstdio> 2 const int N=1002; 3 struct info{ 4 int x,y,t; 5 info(const int a=0,const int b=0,const int c=0): 6 x(a),y(b),t(c){} 7 }im[100001]; 8 int n,m,k,out[N],ti[N],last[N],sum[N],lon[N],ans; 9 int max(int x,int y){ return x>y ? x : y; } 10 void work(){ 11 while (k){ 12 int x=0,y,z; 13 for (int i=n,la=n,min=100000;i>=1;i--){ 14 if (sum[la]-sum[i]>x && lon[i+1]){ 15 x=sum[la]-sum[i]; 16 y=i; 17 z=min; 18 if (lon[i+1]<z) z=lon[i+1]; 19 } 20 if (i<n) 21 if (min>ti[i]-last[i]) min=ti[i]-last[i]; 22 if (ti[i]<=last[i]){ 23 la=i; 24 min=100000; 25 } 26 } 27 if (z>k) z=k; 28 k-=z; 29 ans-=x*z; 30 lon[y+1]-=z; 31 for (int i=y+1;i<=n && ti[i]>last[i];i++) ti[i]-=z; 32 } 33 } 34 void maketi(){ 35 for (int i=1;i<=n;i++){ 36 sum[i]=sum[i-1]+out[i]; 37 ti[i]=max(ti[i-1],last[i-1])+lon[i]; 38 } 39 } 40 int main(){ 41 scanf("%d%d%d",&n,&m,&k); 42 int x,y,z; 43 for (int i=2;i<=n;i++) scanf("%d",&lon[i]); 44 for (int i=1;i<=m;i++){ 45 scanf("%d%d%d",&x,&y,&z); 46 im[i]=info(y,z,x); 47 last[y]=max(last[y],x); 48 ++out[z]; 49 } 50 maketi(); 51 for (int i=1;i<=m;i++) ans+=ti[im[i].y]-im[i].t; 52 work(); 53 printf("%d",ans); 54 }