模拟费用流

其实是读了NOI2021集训队论文《浅谈OI中的模拟费用流问题(陈扩文)》中的一小部分随便翻译一下写点东西。

模拟增广路

直接模拟每次求增广路以及加入反向边的过程,一般是选择一些点当关键点然后用堆来维护两两之间的路径,大多数题讨论起来比较麻烦。

可以参考 [NOI2019] 序列 [AGC018C] Coins 都是非常典的题目。

费用流的凸性

大家都知道费用流算法是每次求一条最短路,然后加入反向边,然后继续求。

设函数 \(Cost_G(k)\) 表示在求 \(k\) 次最短路的费用总和,那么这个 \(Cost_G\) 是下凸函数。

证明:显然证明 \(Cost_G\) 是下凸函数和证明每次求出的最短路单调不降是等价的。

考虑每次最短路会发生什么变化:

1.加入反向边。

2.删去最短路上的边。

设加入要一条边连接 \(x,y\) 距离为 \(z\) 的边,这条边一定在最短路上。也就是 \(dist_x+z=dist_y\),此时加入反向边后,因为 \(dist_y-z\le dist_x\),故不会产生松弛。

而这个过程显然没有产生负环。

在删去最短路上的边后剩下的路径肯定不比最短路短,由于没有产生负环,所以最短路长度单调不降。

所以有的时候不需要直接建出图跑费用流,可以通过wqs二分或者三分来解决问题。

重点来讲一下这个东西的应用: [BJWC2018] 餐巾计划问题

首先大家都做过弱化版。

但是这个题目我们提出另外一种构图方式。下面的 \((a,b,c,d)\) 表示从 \(a\)\(b\) 连了一条容量为 \(c\) 费用为 \(d\) 的边。

\(S,T\) 表示起点和终点,\(A_i\) 表示第 \(i\) 天干净的毛巾,\(B_i\)表示不干净的。

1.加边 \((S,A_1,+\infty,p)\)\((A_n,T,+\infty,0)\)

因为要买毛巾利用率高,最优,所以是 \(S\) 连向 \(A_1\)

2.加边 \((A_i,B_i,r_i,-\infty)\)\((B_i,T,+\infty,0)\)

费用为\(-\infty\) 表示一定要满足这个条件,否则答案一定会多几个 \(\infty\)\(B_i\)\(T\) 表示不清洗毛巾。

3.对于 \(i<n\) ,加边 \((A_i,A_{i+1}, +\infty ,0)\)

4.对于 \(i+m_1\le n\) ,加边 \((A_i,A_{i+m_1}, +\infty ,c_1)\)

5.对于 \(i+m_2\le n\) ,加边 \((A_i,A_{i+m_2}, +\infty ,c_2)\)

跑最小费用可行流即可得到答案。

假设我们最开始购买了 \(x\) 个毛巾,那么发现答案即为\(Cost_G(x)\),所以我们可以三分答案。

现在问题变成了最开始购买 \(x\) 个毛巾,花费的最小值。当我们选择的时候,肯定优先选择使用购买的没用过的毛巾,然后使用慢洗 (便宜) ,最后使用快洗。

我们维护三个序列按照进入时间从小到大排序的序列,表示还没洗好的毛巾,快洗好但慢洗没好的毛巾,慢洗好的毛巾,如果一个没洗好的毛巾快洗完了,转移到快洗完了慢洗没完的序列里,一个没慢洗好的毛巾洗完了,就从转移到慢洗好的毛巾的序列里。也就是从序列的头部加入另一个序列尾部。

而如果我们要选快洗的毛巾的话,一定是选最晚洗好的毛巾,因为如果选之前的就相当于浪费了时间。也就是从序列的尾部操作。所以只需要维护三个双端队列,单次时间复杂度 \(O(n)\) ,总复杂度 \(O(nlogn)\)

代码

维护折线

如果可以将除开起点和终点的点集划分成两个点集,且两点集之间有且仅有一条边,那么这条边的流量和一侧的最小费用是下凸函数关系,所以我们可以维护子图的凸函数的差分(也就是折线)来解决问题,具体例子见 《浅谈 OI 中的模拟费用流问题》第四节读后感 中老鼠进洞的第二种解释。

posted @ 2025-02-10 18:56  QZJ123456  阅读(56)  评论(0)    收藏  举报