模拟费用流
其实是读了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 中的模拟费用流问题》第四节读后感 中老鼠进洞的第二种解释。

浙公网安备 33010602011771号