CF1913 D,E 题解

D:

题意:给你初始数列a,你可以无限次操作,每次操作将任意一个区间只保留最小值,问最后留下的数列有几种。

本以为是道排序后数学题,发现转化不成。

Solution:

很容易将题意中的任意区间转化为长度为2的小区间,即,大小数相邻可以删去大数。

是个DP,每次添加一个数,更新一下前 \(i\) 个数的答案 \(f[i]\)。考虑这个新加进来的数可以带来哪些新结果,那么分为两种,最后这个数被前面更小的数干掉,以及最后这个数留着。

1.这个数被前面的小数干掉的话,它们中间的数肯定也都不在了,找到 i 位置前第一个比它小的数,位置设为 j ,则被干掉的情况方案就是前 j 个数的方案: \(f[i] += f[j]\)

2.如果这个数留着,它可以将 i 到 j 中间的数消去,可以在中间任意的位置停下,我们发现只设一个 f 数组的话很难去重,再设一个 \(g[i]\) ,表示第 i 个位置的数留下,前 i 个位置的方案数。这种情况的转移:\(g[i] = g[i-1] + g[i-2]+...+g[j+1]+f[j]\) ,为什么是这样转移,因为除了 \(f[j]\) 这个中间数全删掉的方案数外,新加入的 i 可以产生很多新状态,即那些将尾部数字消去的方案,那些方案可以用强制第 k 个数保留的表示方法来去重,即这些 g 相加。

然后将 \(g[i]\) 加给 \(f[i]\)。用 sum 记录 g 的前缀和,O(n) 转移。

my submission

E:

题意:给出初始01矩阵,以及每行每列1的数量的目标,每次翻转一个数花费1,求达到目标的最小花费。

Solution:

一眼流。一开始想的是行连源点,列连汇点,但发现最小割跑完后那些残留的行或列不一定能用2个贡献来转移,比如:

3 2
1 0
0 1
0 1
1 2 0
1 2

我们发现列已经满足了,然后第二行少一个,第三行多一个,但将第三行某个 1 移动到第二行这个想法是不可行的,因为两行张的完全一样,这个样例答案不是2,而是4。

如果原数据是:

3 2
0 0
0 1
0 1
1 2 0
1 2

因为第一行缺1,第一列也缺1,那么我们按最小割跑完是会把第一行第一列填成 1 的,而实际的答案这个位置却是0,并不是我们填1不对,而是我们需要有个反悔的机会,而最小割流完之后的残留图没给我们这个机会。

于是考虑怎么构造费用流(这玩意儿就可以处理反悔了)。

我们发现上面的第一个数据,我们的目标就是把第三行多出来的运到第二行,这个过程我们通过改变一些位置的值来实现,如果那些位置在跑费用流之前没改变过(比如\((2,1)\) ),那么花费1,如果那些位置之前改动了(比如第二个数据的\((1,1)\)),那么花费-1,相当于撤销这个改动。

那么结合这两个样例,思路大概有了:我们先自己动手改变一些位置,强制让列符合目标,然后会有一些行需要加,一些列需要减,分别连向源点和汇点。如果加减量不一样,或者无法满流(如下),那么无解。

2 2
0 0
0 0
2 0
2 0

那么中间的路径怎么构造:如果这个位置是0,从列连向行,如果这个位置是1,从行连向列。如果这个预处理时变过,费用是-1,表示反悔,否则费用是1,表示花费。

就拿我上面第二个样例来造图,因为要先强制翻转\((1,1)\),于是这条边花费是-1,其他边花费是1,连接源汇点的流量就是这行缺多少1,或者剩多少1:

这根绿色的路径就是我第二个样例的答案,费用是2,中间流经的每一根线就是翻转的位置。

建完图直接跑费用流,记得加上一开始自己翻转的那些花费。

my submission


题外话:这题我一开始T了,以为是出负环了,去学了下带负环的费用流(还要转成上下界),结果样例过不去了,才发现是cnt忘了设成1,哈哈我怎么过的样例。

又仔细思考了下,我这种建图方式虽然有负费用,但是不会有负环,证明需要好多字,懒得写了。


总结下这种题的做法,就是根据自己以往的做题经验,建图方式努力往上靠,无非就是两种:1.行连源列连汇 2.收支平衡,亏连源,盈连汇。

然后具体该如何构造一个自动计算花费的网络流,就是需要自己灵光一现了。但是,在以往经验的基础上,你的建图已经有了一个大致的框架,也就容易灵光一现了。比如这题的建图还是比较简单的,我觉得是灵光一现,别人可能就觉得是典型,就是经验积累量的区别了。

posted @ 2024-01-16 18:06  maple276  阅读(13)  评论(0编辑  收藏  举报