题解:P8387 [COI2021] Autobahn
Error_Eric · · 题解
Statement
Link
题目的意思有点迷,附上样例
Sol
注意到 (严格来说,
有一个在此类套路中常见的结论,就是最佳选择一定可以让其中一段落在线段的断点上。证明也很简单:
不妨假设一段区间
[l,r] 是最优的,且两端都不在断点。那么往左滑动一格价值(减免费用)的减小量(必然非负)肯定等于往右滑动一格价值(减免费用)的增加量(必然非正)。则价格变化为0 , 这个区间可以一直滑动直到碰到断点即可。
只需要用 尺取法 算出每一段可能选择的价值即可。每段线段只会被加入/删除一次,所以复杂度是
Code
有点丑陋。
n, k, x = map(int, input().split())
events = []
for i in range(n):
li, ti, ri = map(int, input().split())
events.append((li, 1))
events.append((ri + 1, 2))
if li + ti < ri+1:
events.append((li + ti, 4))
events.append((ri + 1, 3))
events.sort(key = lambda x: x[0] * 5 + x[1])
totalcount, expirecount, ranges = 0, 0, []
for ei in events:
if ei[1] == 1: totalcount += 1
elif ei[1] == 2: totalcount -= 1
elif ei[1] == 3: expirecount -= 1
elif ei[1] == 4: expirecount += 1
add = (ei[0], expirecount if totalcount >= k else 0)
if len(ranges)>0 and ranges[-1][1] == add[1]:
pass
elif len(ranges)>0 and ranges[-1][0] == ei[0]:
ranges[-1] = add
else: ranges.append(add)
events = None
def value(pos : int, r: list) -> int:
if pos == len(r) -1: return 0
else : return r[pos][1] * (r[pos+1][0] - r[pos][0])
def fun(e : list):
laste, payment, ans = 0, 0, 0
for i, ei in enumerate(e[:-1]):
while laste+1 < len(e) and e[laste+1][0] <= ei[0] + x:
payment += value(laste, e)
laste += 1
ans = max(ans, payment + e[laste][1] * (ei[0] + x - e[laste][0]))
payment -= value(i, e)
return ans
a1 = fun(ranges)
ranges2 = [(0,0)] + [(ranges[-1][0] - ranges[i][0] +1, ranges[i-1][1]) for i in range(len(ranges)-1, 0, -1)] # 只枚举了左端点,所以反过来再做一遍
a2 = fun(ranges2)
print(max(a1, a2))