Description
新年将至,C 校的 (m) 位同学们由于在家的生活过于无聊,他们每个人都购买了一个快递。
他们都住在一条街,这条街有(n) 个小区,第 (i) 个小区 ((1le i le n)) 只与第 (i+1) 个小区相连,到第 (i+1) 个小区的距离是 (d_i) 。第 (i) 位同学住在第 (p_i) 个小区,这位同学的暴躁程度是 (d_i) 。
你作为快递公司的老板,你想在新年前的最后一天准备干完这 (m) 个同学的单子就跑路。快递公司正好有 (m) 个邮递员,每个邮递员在最后一天可以将至多一个人的快递(具体是谁的可以由你指定)送去第 (q_i) 个小区,然后叫这个人去领快递。
如果一个邮递员在新年的最后一天工作了,他会有 (c_i) 的不开心值,而见到同学之后,因为这位同学十分暴躁,又走了一段漫长的路程,他会传递给这个快递员 ( ext{这个同学的暴躁程度}+ ext{这位同学所走的路程}) 的不开心值。而快递员将索要等同于他的不开心值的小费。
迫于压力你只好咕掉某几个同学的快递,想到知道对于所有 ((1le i le m)) 如果发送了 (m) 个同学的快递,你最少需要支付多少小费。
(n le 800 , mle 50000)
Solution
首先可以简单得跑网络流得到 (30) 分的好成绩。
然后考虑如何把复杂度优化成 (O(nm)) 的。
显然无法避免 (m) 次增广,所以考虑把一次增广的时间复杂度优化成 (O(n)) ,首先考虑一种建图方法:中间的 (n) 个点表示 (n) 个小区,然后在第 (i) 和第 (i+1) 号点之间连一条双向边,费用为 (d_i) ,流量为 (inf) ,然后对于每一个同学,连一条边 (s ightarrow p_i) ,费用为 (d_i) ,流量为 (1) ,然后对于每一个快递员,连一条边 (q_i ightarrow t) ,费用为 (c_i) ,流量为 (1) 。
显然,对于第 (i) 个点,(s
ightarrow p_i) 和 (q_i
ightarrow t) 只有权值最小的那一条边有用,且一条边没走过后不可能被退流,于是考虑每次只把有用的边放进去跑 spfa 增广。
于是我们得到理论复杂度 (O(knm)) 的做法,其中 (k) 是 spfa 中每个点入队的平均次数, 看上去非常能过,然后你发现你一个大数据跑了 (5s) ,然后你看了一下每个点的入次数,发现他没有很大,出题人良心地没有卡 spfa。于是你尝试着卡了一波常,卡到了 (1.5s) 之后发现卡不动了。
卡不动的主要原因是,这个做法的常数太大了,我的卡常技术不足以把他卡过去。
考虑一种更优的做法,记录正着走(从 (i) 走到 (i+1) ),和倒着走的可以退流的次数,然后每次通过正反各扫一遍的方法找到最短路进行增广,以正着扫举例,具体做法是记录最近的能到达当前点的最近的同学,然后和当前点的快递员匹配,每次更新离当前点最近同学的距离。
于是我们得到了一个复杂度是真的 (O(nm)) 的做法。