这题就是个贪心,但是细节决定成败(给跪了)
每个人到起始站的时间为s[i],到达的目的地为t[i],
arrive[i]表示到第i站的时间,last[i]表示第i站最后一个人到达的时间(读入时O(m)处理)
则每个人在车上的时间=到达目的站的时间-这个人到达起始站的时间
即n个人的总时间ans:=sigema(arrive[t[i]]-s[i])(1<=i<=m)
易知每一站的到站时间只由从前一站离开的时间决定
易得arrive[i] = max{arrive[i-1], last[i-1]} + d[i] (道路的编号与所连的右点相同,即d[i]为从i走到i+1所用的时间O(n)的时间求出arrive[i]
当我们令d[i]减少1时,很显然我们一定会影响到在第i站下车的人即ans-num[i](num[i]表示第i站下车的总人数)
同时很显然我们一定不会影响[1,i-1]站
那么我们会不会影响第i站以后的人呢?
一定是可能的
那么,影响到后面的站的条件是什么呢?
易知影响后面的站的到站时间取决于离开i站的时间,如果离开i站的时间可以更优那么我们就能影响到后面的站
离开时间为max{last[i],arrive[i]},
而last[i]是不变的
所以只有当last[i]<arrive[i]的时候离开时间才可以更优,
不然再怎么优化除了对第i站的影响,你的离开时间不变对后面不会造成影响
所以每一条道路的修改都会至少影响一个点(i站)或一个区间,而一个点可以表示为[i,i]
我们用change[i]表示修改第i条道路会影响的人数和r[i]表示它所影响的区间右端点(左端点一定是i)
如果last[i]>arrive[i]那么r[i]=r[i+1],change[i]=num[i]+change[i+1]
否则r[i]=i,change[i]=num[i]
所以我们从后往前O(n)更新求值
然后每次选择一个最大change[i],即影响人数最多的道路,
令d[i]在满足要求的前提下能减少多少就减少多少即可
再在ans中减去最大所用的加速器tt*chang[i]
每次修改后 要重新计算新的arrive[i]、change[i]、r[i]
时间复杂度O(kn)(其实是到不了的)
=w=接下来是决定你能不能AC的细节(给跪了QAQ);
先来看题目要求:
1、最多用k个加速器
2、保证d[i]>=0
再来看贪心思路的要求:
1、每次挑一个最大change[i],能减多少减多少的要求:
(1)修改后第i条道路仍然可以影响到[i,r[i]]的车站
即第i站到第r[i]-1的arrive>=last
并且注意不要把r[i]也算进去,因为反正i只能影响到r[i]后面的影响不到(即arrive[i]<=last[i])
(2)当我们把k个加速器都用完后我们就完成了加速,退出循环循环输出答案
(3)所有的道路都变成了0而k没有用完也直接退出
(4)在找到最大change[i]的时候要保证第i条道路不能为0不然的话就会死循环(因为每次都找到它但是每次都不能修改)
(5)但是在每次更新change[i]和r[i]的时候我们不需要考虑它的d[i],因为即使d[i]=0但是它左面的点可能也影响到这个站所以更新方式不需要考虑d[i],不然会让左面的站影响人数和范围减少
然后在各种wa红、TLE红以后AC绿变得格外亲切=。=
var
n,m,k,tt,x,maxn :longint;
i,j :longint;
ans :int64;
d :array[0..1010] of longint;
s,c,t :array[0..10010] of longint;
last :array[0..1010] of longint;
change,arrive,num,r:array[0..1010] of longint;
function max(a,b:longint):longint;
begin
if a<b then exit(b) else exit(a);
end;
function min(a,b:longint):longint;
begin
if a<b then exit(a) else exit(b);
end;
begin
read(n,m,k);
for i:=2 to n do read(d[i]);
for i:=1 to m do
begin
read(s[i],c[i],t[i]);
if (s[i]>last[c[i]]) then last[c[i]]:=s[i];
inc(num[t[i]]);
end;
for i:=2 to n do arrive[i]:=d[i]+max(arrive[i-1],last[i-1]);
for i:=1 to m do inc(ans,arrive[t[i]]-s[i]);
//
while true do
begin
arrive[1]:=0;
for i:=2 to n do arrive[i]:=d[i]+max(arrive[i-1],last[i-1]);
//
change[n]:=num[n];
if d[n]>0 then
begin
maxn:=num[n];x:=n;
end else maxn:=-1;
r[n]:=n;
for i:=n-1 downto 2 do
begin
change[i]:=num[i];
r[i]:=r[i+1];
if (arrive[i]>last[i]) then change[i]:=change[i]+change[i+1]
else if (arrive[i]<=last[i]) then r[i]:=i;
if (change[i]>maxn) and (d[i]>0) then
begin
maxn:=change[i];x:=i;
end;
end;
//
if maxn<0 then break;
tt:=maxlongint;
for i:=x to r[x]-1 do tt:=min(tt,arrive[i]-last[i]);
tt:=min(tt,k);
tt:=min(tt,d[x]);
//
dec(d[x],tt);
dec(k,tt);
dec(ans,tt*maxn);
if (k=0) then break;
end;
writeln(ans);
end.
——by Eirlys
转载请出名出处=w=