[ZJOI2015]醉熏熏的幻想乡

本文详细介绍了ZJOI2015中一道题目的解题思路,利用网络流解决最大流问题,并探讨了在保证总费用最小的情况下,如何通过贪心策略和分段一次函数来优化解决方案。文章还提及了算法的时间复杂度和代码实现,并分享了一个关于该题目背后的故事,揭示了出题人的东方文化影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

醉熏熏的幻想乡

题解

相当阴间的一道题

首先如果只用解决第一小问的话,我们很容易想到通过网络流来求最大流的。
但是我们现在还要保证总费用最小呀,这该怎么办?拆成许多个点费用流,不 T T T才怪。
我们假设图是一个完全二分图(就是左侧所有点与右侧所有点间都有边),我们是不是可以贪心地解决。
我们考虑对于每个极小单位流量,都选择当前最优的一个点选择,那么我们每次选择的点就是当前流量最小的点。
也就是说,我们选择的这个点当前的导数最小的点,这样的话,我们选择到最后,一定是所有选择了的点导数全部一样,而所有没有选择的点,导数肯定不小于选择了的点的导数。
我们原式是一个二次函数呀,那它的导数也就是一个一次函数,其现在的真实值就是将它现在的产量带入这个一次函数嘛。
那样的话岂不是就可以直接二分出我们这个导数的使它跑满的最大值,也就知道现在每个点的贡献,可以计算答案了。

でも(demo),这是对于一个完全二分图呀,我们的图不一定是一个完全二分图呀。
也就是说,实际上并不是每一个函数最后的导数都相同。
不过我们可以发现,如果我们记 f ( λ ) f(\lambda) f(λ)表示我们导数的最大值为 λ \lambda λ,我们的最大流量的话,我这个 f ( λ ) f(\lambda) f(λ)应该是一个 O ( n ) O(n) O(n)级别分段的分段一次函数。
我们先就按原本最大流(最优费用时的流的状况)时,每个点对每个点的流量贡献来看每个点的流量填充情况,显然,在我们的 λ \lambda λ没有达到当前函数导数最终的最大值时,它是不会影响到别的点的贡献流量的。如果达到最大值后,即使我们 λ \lambda λ的增长,它也不会对其他点产生新的贡献,不然的话最大流的情况下它为什么不贡献呀。
也就是说,当我们这样划分后,不同点的贡献就独立开来了。
它的贡献会在我们的 λ \lambda λ达到某个阈值时开始增长,达到某个阈值后停止增长。
而它在阈值内的增长,显然单位费用是关于一个流量的一次函数,也就是原二次函数的导。那么我们将单位费用设为自变量,那么流量也肯定是一个一次函数。
只看一个点,它关于 λ \lambda λ变化,肯定是一个一次函数嘛。当然是一个分 3 3 3段的一次函数 ,具体怎么描述好像笔者画不来图
既然一个点的 f ( λ ) f(\lambda) f(λ)是一个分段一次函数,那么我们将所有点的 f ( λ ) f(\lambda) f(λ)累加起来,也应该是一个分段的一次函数,并且总的分段是 O ( n ) O\left(n \right) O(n)级别的。
我们将这个分段函数算出来不就可以积分算答案了吗?

但这么算这个分段函数呢?
首先,我们可以想到一种方法算每个单点处的函数长相。
假设我们要算的是费用为 λ \lambda λ处的 f f f函数长啥样,我们可以这样做。
我们将所有点都向 S S S连一条它的导数在不超过 λ \lambda λ情况下的最多贡献的流量,然后跑最大流,我们看我们现在的割边情况。
如果我们现在左侧点到 S S S的边被割掉了,也就是说这个点的流量在这个点是满的嘛,那么就会贡献到我们 f f f函数的常数项上。
但如果流满了,但是它本身还可以继续流呢,也就是 c i c_i ci没满。那不就是我们在增加 2 a i 2a_i 2ai的费用,就会增加 1 1 1的流量嘛(只看这个点的趋势),那肯定就是给 f f f加上一个一次函数呀。
如果它没被割掉,那割掉的肯定是右侧点嘛,右侧点既然流满了,那也就说明它是一个常数项,直接加到 f f f的常数项上。
这样不就求出了我们单点上的函数了嘛。
但我们怎么求整个分段函数呢?不妨分治处理。
如果我们左端点与右端点的函数不一样,那不就说明他们之间有一个新的函数。
我们可以将左右函数的交点拿出来,在这个交点这里往两边二分。
不过如果是看两边函数是不是一样的可能有点问题,其实应该是将它们的交点的左侧函数与右侧函数求出来,看是否与原来的左函数与右函数一样。

这样我们就轻松的求出了整个分段函数的长相,然后就可以积分算答案了。
不过由于都是一次函数,根本没必要积分,小学数学就可以算面积了。

时间复杂度O ( n ∣ 网 络 流 ∣ ) \left(n|网络流|\right) (n),我们分治其实也只求了端点处的函数,所以是 O ( n ) O\left(n\right) O(n)级别的。

番外

代码在下面,这里讲一个从老师那里听来的故事(真实性不可考)
众所周知,ZJOI2015 是东方场 这就是OIer界车万的力量吗
本题出题人clj理所当然是一个车万。
据说,当年在clj初二的时候,他还不是像现在这样强大。当时,笔者学校有一个非常强的OIer,这个OIer是一个车万,它出题经常都会带许多东方背景。
而clj当时与这个OIer有许多接触(或许是崇拜?),然后就理所当然得沾染了许多东方的元素,这对他以后的出题风格造成了许多影响,导致他也出了许多东方风格的题。
于是,就有了我们现在所看到的ZJOI2015。
不要再永琳(erin)啦!关于网易云循环えーりん!えーりん!助けてえーりん!的这码子事。

源码

几乎是照着 h e h e z h o u \rm h\red{ehezhou} hehezhou代码写的,因为基本上都是从代码中学到做法。不是我讽刺他的题解,两者参照啦!!!

#include<bits/stdc++.h>
using namespace std;
#define MAXN 105
#define MAXM 205
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值