模拟费用流学习笔记

前情提要:上个学期开了一道老鼠进洞的题,然后一直咕到了现在……

大概就是把费用流建出来,然后利用一些性质,贪心来实现整个费用流的过程,即找最短路,退流等。

很难想到,但大多都是费用流比较显然,然后数据范围比较大的题(目前做到的好像都是和“匹配”相关的)

参考WinnieChen的博客,代码实现可以在那边看orz

老鼠进洞,代价为距离,求最小代价

problem 1:每个老鼠只能往左走

拆贡献,每一个老鼠贡献肯定是 $x_i$ 所以每次匹配只要取能取的最小的 $-y_i$ 即可

problem 2:老鼠左右不限

早上看了 $laofu$ 的WC课件 $dp$ 的做法,感觉就是一个利用单调性的 $dp$ 优化,结果下午才发现原来他把网络流相关的做法放到最后面了…

先整体排序以后 $dp$ ,除了0之外的决策唯一是因为状态最终的意义是到0,都是等待后面的鼠或者洞把它抵消变成0,所以新的值取值一定是负,那么越后面取一定更优

然后用差分数组 $d_{i,j}$ 表示 $f_{i,j}-f_{i,j-1},j>0$ ; $f_{i,j}-f_{i,j+1},j<0$

感性理解一下这个 $f$ 的值是凸的(多出来的尽量先选负值更小的,后面越来越大),所以只要对于 $d_{i,j}$ $j$ 大于等于0和小于0各维护一个堆,取最小值,就可以动态维护 $f_0$ 的值并且加入新的决策值了(新的 $d$ 只会从零点产生)

考虑这个东西,我们可以对于老鼠和洞建出一个网络流。

可以发现堆里面的三个操作实际就是用贪心实现了网络流:

1.假如是老鼠,那么选左边的最近的洞匹配,即网络流的增广。

2.假如是洞,那么看它是否可以反悔前一个老鼠的操作,即让老鼠向右匹配,就是退流以实现最短路。

3.向右匹配可能会带来的后果是再右边的要向左匹配,会产生交叉,实际答案是把交叉部分剪掉,要在 $Q1$ 中新插入一个洞,相当于反向边。

补充:对于最小费用最大流问题,可以对于每个老鼠先带一个权值 $-\infty$ ,后面再加回来,就是一个最小费用流问题了。(虽然不知道这道题有什么用qaq)

problem 3:老鼠向左走,每个洞有代价,老鼠不一定要匹配,最大化代价

这个东西拿个堆随便做,拆贡献,老鼠每次选代价最大的,为了反悔再插入 $-x_i$

problem 4:每个洞可以进 $b_i$ 次

还是一样用堆搞,只不过再存一个剩余容量,直到全部被减成0了再退出堆。

分析复杂度,每一个老鼠最多只会上手自己匹配一次,然后被堆反悔一次,还是 $O(nlogn)$ 的

problem 5:老鼠无限分身,洞容量无限,老鼠和洞都要被匹配

对于这种的流量无限,但是必须匹配最小代价的题有一个trick:先对于每一个点拉出一个流量为1权值为 $-\infty$ 的第一类点,剩下的为流量无限的第二类点,可以保证所有都被匹配,然后还是一样用堆做,此时洞和鼠已经一样了,所以鼠也可以使洞反悔。

因为不可能两个第二类点匹配,用分身肯定只是让周围的更近匹配,所以反悔操作次数还是 $O(n)$ 的。

problem 6:在problem2的基础上加上每个洞有代价

考虑problem3的反悔,就是意味着一个鼠它如果匹配了右边的洞,有可能因为代价原因继续向右反悔,所以在右边的洞匹配左边的点时,不仅要在 $Q1$ 加入反向边新产生的洞,还要在 $Q0$ 加入可供后面洞反悔的鼠。

例题

XJ省选模拟8-B

感觉这个难度放第一题有点不合适啊qaq

建立费用流,数轴上顺序连出左右括号,起点往左括号连一个费用边,右括号向终点连一个费用边,建一个虚点限制流量。

然后模拟这个过程:每次跑出一个最短路然后增广。

发现这个图的费用边是不可能被退掉的,所以可以贪心选择。

考虑顺着流,那么就相当于选择一个左右括号,如果反着流,那么就是选择一个右左括号,要求反向边流量至少为1,正向边流量没有限制。

所以可以建立出一颗线段树维护,每次选出代价最小的左右或右左括号。

对于左右括号,因为没有限制,那么直接维护区间答案,单个左括号,单个右括号。

对于右左括号,考虑对于0的限制的比较经典套路是维护区间反向边最小值。

我们把右左括号分成自由的和受限制的,自由的直接更新区间答案,受限制的看区间最小值大于0才更新答案。

这里受限制是指有最小值在右左括号之间,因为不是最小值意味着一定大于0。

所以维护上述的东西,我们可以处理出在分别最小值左、右的单个左右括号,合并的时候讨论一下反向边最小值大小即可。

然后维护这些点的位置,找出两个点后把边权设为 $\infty$ ,增广把区间全部的反向边加1或减1。

雪灾与外卖

大概就是把problem4和6组合一下,然后被hack了,在继续向右反悔的过程中不能一个一个来,不然可以刚开始所有点都匹配第一个洞,然后洞一个一个反悔过去,复杂度就是 $O(n^2logn)$ ,要注意把这些相同的绑在一起搞。

Q1.push(mk(INF,inf));
for(int i=1;i<=n+m;i++){
    if(a[i].typ==1){//
        pii tmp=Q1.top(); Q1.pop();
        ans=ans+tmp.first+a[i].x;
        Q0.push(mk(-tmp.first-2*a[i].x,1));
        tmp.second--;
        if(tmp.second) Q1.push(tmp);
    }
    else{//
        int cnt=0;
        while(!Q0.empty()&&Q0.top().first+a[i].x+a[i].w<0&&a[i].c){
            pii tmp=Q0.top();
            Q0.pop(); 
            int now=min(tmp.second,a[i].c);
            Q1.push(mk(-tmp.first-2*a[i].x,now));
            ans+=(tmp.first+a[i].x+a[i].w)*now;
            a[i].c-=now; tmp.second-=now;
            if(tmp.second) Q0.push(tmp);//!!!
            cnt+=now; 
        }
        if(cnt) Q0.push(mk(-a[i].x-a[i].w,cnt));//!!!
        if(a[i].c) Q1.push(mk(-a[i].x+a[i].w,a[i].c));
    }
}

 NOI2019序列

[ICPC2018 WF]Conquer The World

后两题的题解代码网上自取,有手就行

posted @ 2021-04-06 19:00  'Clovers'  阅读(335)  评论(0编辑  收藏  举报