模拟流的问题小结
模拟流的问题小结
模拟流的问题一般都是通过建出流的模型,观察流的性质然后用数据结构高效模拟流。
Codeforces 280 D. k-Maximum Subsequence Sum
有一个长度为 \(n\) 的序列,单点修改,维护在这个序列中选取至多 \(k\) 个互不相交子序列的权值和最大值,\(n \leq 10^5, k \leq 20\)。
拆点后建出费用流模型
观察发现,每一次增广路是 \((i,i+n,1,a_i)\) 边的最大连续子段和,那么我们可以通过维护最大连续子段和来寻找一次增广路,反向边操作只需要对选出来的区间取反,线段树直接维护即可。
2019 Multi-University Training Contest Saltyfish
有一棵大小为 \(n\) 的有根树,每个点有 \(a_i\) 个苹果,有些点有摄像头可以监控其子树内距离它 \(\leq d_i\) 的点,被监控的苹果就不能选取了,破坏这个摄像头需要 \(c_i\) 的代价,要求最大化选取的总苹果数 \(-\) 总代价。\(n \leq 3 \times 10^5\)。
经典的最大权闭合子图模型
答案是这个图的 \(\sum a_i -\) 最小割。
观察发现,一个摄像头的流量,一定是尽可能选取它管辖范围内较深的苹果的流量,显然深度较小的苹果的流量更有可能让该摄像头的祖先增广,于是维护一个关于深度的流量信息。这个东西线段树合并维护即可,势能分析一下复杂度是对的。
NOI 2019 序列
有两个长度 \(n\) 的序列 \(A, B\),需要在两个序列中各选取 \(k\) 个数,且满足两个序列选出的公共位置数 \(\geq L\),\(n \leq 10^6\) 。
费用流模型对我来说不太容易想到,关键在于建一对中间点 \(C, D\) 来限制不公共的选取。
建出费用流以后比较无脑,暴力枚举所有增广路的可能。
-
\(S\rightarrow i\rightarrow i+n\rightarrow T\)
-
\(S\rightarrow i\rightarrow C\rightarrow D\rightarrow i + n \rightarrow T\)
-
\(S \rightarrow i \rightarrow i+n \rightarrow D \rightarrow j + n \rightarrow T\)
-
\(S \rightarrow i \rightarrow C \rightarrow j \rightarrow j + n \rightarrow T\)
-
\(S \rightarrow i \rightarrow i + n \rightarrow D \rightarrow C \rightarrow j \rightarrow j + n \rightarrow T\)
这时候先考虑维护流量,观察发现 \(S\rightarrow i,i+n \rightarrow T\) 的流量实际意义是 \(A_i/B_i\) 是否被选,\(i\rightarrow i + n\) 是流量的实际意义是 \(A_i, B_i\) 是否同时被选, \(i\rightarrow C, D \rightarrow i\) 及其反向边是否有流量的实际意义是 \(A_i/B_i\) 是否作为一个无序点对被选择了,那么我们只需要控制一下增广的总流量不超过 \(k\) ,单独维护一下 \(C \rightarrow D\) 的流量即可。
那么每一次找增广路,只需要根据这五种增广方式所走的增广路对应的实际意义选出两个点即可,同时维护一下 \(C \rightarrow D\) 的流量,注意第五种情况这条边会退流。每次选出两个点需要用若干个堆维护,细节有一点多。
在实际模拟过程中,我发现不加第五种增广也是可以过的,但是不会证明,就先写到这里好了。
注意实际上存在第 \(6\) 种增广形式,即先前让一个公共对流了 \(C \rightarrow D\) 的边,之后增广把这条流量退流,这个东西模拟起来比较麻烦,但因为公共对是否流 \(C \rightarrow D\) 边的费用一样,所以只需要控制公共对不流 \(C \rightarrow D\) 边即可。具体得分视常数 \(80-100\) 不等。
UER 8 雪灾与外卖
有 \(n\) 个老鼠,\(m\) 个洞,洞有流量 \(cap\) 和费用 \(cost\) ,老鼠到一个洞里的代价是 \(|x_i-y_j|\) ,求所有老鼠走到洞里的最小代价。
这个题的模型是经典二分图带权匹配,但是和前面几个题不一样的是,建出这个模型对于解题并没有什么用。我们可以用一个比较显然的结论进行推导,然后套上WC讲课的算法。
结论:匹配边不会交叉,因为交叉显然代价比不交叉要大。
我们从左到右考虑老鼠和洞,遇到老鼠和洞的时候钦点其匹配左边的老鼠和洞。对于老鼠,必须要匹配一个洞,如果不存在这样子的匹配,就令它匹配一个代价是 \(\inf\) 的洞,然后老鼠就可以反悔了,每次反悔相当于撤销掉这次匹配的贡献然后为下次匹配提供贡献。对于此时的洞,其没有资格反悔,因为其一旦反悔,当前这只老鼠就失去了匹配,若是其匹配的老鼠反悔后,这个洞要匹配之后的老鼠也是不可能的,因为这样子匹配就会交叉。
如果遇到一个洞,其向左匹配老鼠相当于不断让左边的老鼠反悔直到不优,注意此时洞和老鼠都是可以反悔的,老鼠的反悔同上,洞因为匹配了左边的老鼠,其可以让匹配的老鼠匹配回在加进这个洞之前的状态,然后匹配这个洞右边的老鼠。对于一个洞,其匹配的每一只老鼠进行反悔的代价都是一样的,所以可以一起处理,用堆维护任意时刻左边的老鼠和洞的反悔操作,复杂度一个 \(\log\) 。
ICPC World Finals 2018 征服世界
有 \(n\) 个节点的树,树上每个节点有一个老鼠和一个洞,洞有流量,老鼠到一个洞的代价是树上的距离,最小化所有老鼠进洞的代价。
类似于上一个题,从下到上考虑每一个节点,每条边不会被双向经过,所以当老鼠反悔的时候之前匹配的洞无需退流。我们处理 \(lca\) 为它的匹配,对于每一个匹配的贡献是老鼠和洞的深度和减去当前节点的深度的两倍,每次取出以它为 \(lca\) 的一对最优匹配增广,同时加入反悔的操作。用可并堆维护即可。