杂项

保序回归类问题

一类特殊的保序回归类问题。

对于某一类dp问题,dp形式大概是:$$f[i][j]=\max_{k}(f[i-1][k])+w(i,j)$$

\(w(i,k)\) 具有单调性或者凸性,\(f[i][j]\) 对于同一个 i 在第二维的变化上具有凸性的问题。
做法就是维护数组 \(f[i]\) 形成的凸函数图像。

考虑每次新增一个 i 对于答案的影响,设 \(g[i][j]=\max\limits_{k}(f[i-1][k])\) 由于 w(i,j) 具有凸性或者单调性,那么可以理解为在 g 这个函数上加了一个或者两个一次函数。维护拐点的变化。

对于部分的这类问题,可以用STL进行简单维护。其他的也可以用数据结构进行直接维护。

luoguP4597 序列 sequence
题目大意:
给定一个序列,每次操作可以把某个数 +1 或 −1。要求把序列变成非降数列的最小操作次数。
数据范围:
\(1\le n\le 5\times 10^5\)

解法:
考虑列一个正常的dp:

\[f[i][j]=\max\limits_{k\le j}(f[i-1][j])+|a[i]-j| \]

对于这个东西,发现他其实非常满足上面的条件。嗯……其实就是模板题。
那么我们考虑新增一个函数 \(g[i][j]=\max\limits_{k\le j}(f[i-1][j])\),由于原来的那个 \(f[i]\) 是个下凸函数,那么能够得出,这个函数 \(g[i]\) 也是一个下凸函数,不过从某个位置往后就变成了一条水平的直线。
\(|a[i]-j|\) 是一个从 a[i] 位置向两边不断变大的折线,让他加到原来的 g 函数上之后,在 a[i] 左边的函数图像斜率集体减一,右边的函数图像集体加一。
然后就没有影响了。这样当他是 i 的时候的情况就处理完了。
那具体的维护方法。就是维护拐点的个数和位置。
能够发现的是,这个图像两个相邻斜率的大小只相差 1,通过这点,我们可以通过维护节点个数来直接求出某个点的斜率大小。
那么我们得到 g 函数的过程,就是让右边的部分一直弹出,直到弹到某个位置的图像是水平的为止。
接下来我们就直接在 a_i 的位置加两个拐点就可以了。
用堆维护,时间复杂度是 \(O(n\log n)\)

这里记录一下左偏树的做法。

感性理解题意:
给定一个序列 a,要求经过若干次 +1 或 -1 的操作,使得序列变成一个单调递增(不降,递减或不增)的序列,求最少的操作次数。

解法:
首先就是如果题目中有严格的限制,先把这个严格的限制去掉,即每个数减去自己的下标,由 \(a_i\) 变成 \(a_i-i\)
呃,接下来依旧是感性理解:
显然的是,我们可以把原序列划分成若干段递增或递减的序列(就是一段增,一段减这样拼凑起来)。
对于一段单调不降序列,那么我们就不对他进行操作,让他保持原来的数,正确性显然是对的。
对于一段单调不增序列,我们把这段数的中位数取出来,然后让这一段都等于这个中位数,正确性应该也比较显然。
之后我们把这一段段序列合到一起,我们发现对于划分后的序列就算是满足条件,合并后也不一定满足。
但是,我们可以重复之前的操作,使他最终变成一个单调的序列。
至于正确性,我感觉显然,但我不会证明。

总结一下上面的东西,我们发现是对于任意的一个时刻,我们的单调不降序列可以忽略,单调不增序列就求中位数。
对于求中位数这种东西,可以直接用左偏树做,是吧。
然后就做完了,复杂度:\(O(n\log n)\)

给几个例题的链接:
luoguP4331 [BalticOI 2004]Sequence 数字序列
luoguP4597 序列 sequence
luoguP2893 [USACO08FEB]Making the Grade G
CF713C Sonya and Problem Wihtout a Legend
好吧,全都一样的题,也是无(lè)语(huài)住(wǒ)了

摩尔投票法

我还以为我写过总结,原来没写过?
求绝对众数的方法。

对于一个数,如果在序列中出现的次数 \(>\frac n2\),我们称这个数为这个序列的 绝对众数。显然是这个序列的众数,且只会有一个。

大体思路就是:因为出现次数严格大于 \(\frac n2\),所以我们可以拿任意两个不同的数相互抵消,在任意抵消的情况下,都不可能把这个数抵消掉,而且最后一定会剩下一个数。所以如果存在众数,那么一定是这个数,如果不存在,那么最后也会剩下一个,此时就需要判定是否是最后一个了。

luoguP7045 「MCOI-03」金牌
题目大意:
交互题,有 n 个数。每次你可以询问交互库 x 和 y,交互库会返回这个位置上的数是否相同,询问最多 Q 次。最终要求输出一个序列,是指的在原序列的下标,使得任意相邻的两个数是不同的。
数据范围:
多组询问。对于每组 \(Q=2n-2\)
\(2\le n\le5\times10^4,1\le T\le 5\times 10^4,\sum Q\le 10^5\)

解法:
感觉还是个很神仙的题吧。
首先先说无解,肯定是有某种数的个数大于 \(\left\lceil\dfrac{1}{2}\right\rceil\),才会是无解。
这个看上去比较特别数字,启示我们想到摩尔投票法。
具体来说
就是拿一个容器,去维护到了当前第 i 个之前,还没有放入序列中的数,如果说这个东西里面有两个不是一个数,那么显然就可以把它拿出来了,放到序列后头去,序列的结尾只会有一种数,所以两个不一样的数是一定能放进去的。所以,放到这个东西里的所有位置,他们位置上的数字一定是同一种。
然后我们每次让位置后移一位。如果说当前这个东西里没有一个数,那么我们那这个位置和序列的最后一个位置比较,询问是否是同一个数。如果是,那么把这个新加的位置放到那个东西里去,否则就放到序列后面。
如果说当前这个东西里有数字,那么就询问是否和这个东西里的数是同一种,如果不是,那么两个依次放入序列中(此时,序列的最后一个数一定和容器里的数是同一种,证明可以自己想想),否则就把这个位置也加入到容器中去。
如果说最后一个数都操作完了,容器里还有位置。那么我们遍历序列中的每一个位置,看看能否找到一个空隙,使得两侧的数和容器中的数不是同一种。找到后就从容器中拿出一个数来放进去就行了。
记住如果序列中第一个和容器中的数不同,还可以把一个位置放到序列的最前头。
最后如果容器里还有数,那么就是无解了。
稍稍证明一下询问次数:
对于一开始的遍历,我们只会让每个位置和前面的某一个位置比较,所以次数是 \(n-1\) 的。
对于第二步找空隙的过程,我们只会遍历当时序列中的所有数,这个数一定是小于等于 \(n-1\) 个的。
所以总步数小于等于 \(2n-2\),可以通过。
时间复杂度:\(O(\sum n)\)

第 k 小问题

常用做法:增量构造。
构造所要满足条件:
1.每个状态有且仅有一个前置状态;
2.前置状态一定在自己之前被取出;
3.每个状态的后继状态只有 \(O(1)\) 个。

一般是先满足条件13,条件2大部分可以通过恰当的排序来满足。
这种做法适用于 \(k\) 不大的时候。

容易发现,这种构造方法使得所有状态构成了一颗树,而求第 k 大,就相当于在树上用堆来bfs的过程。第 k 次从堆中取出来的就是答案。

‘Riliane Lucifen d’Autriche’ teafrogsf 和他的四面镜子
题目大意:
给定大小为 \(n\) 的元素集,从中选出 \(m\) 个组成子集,子集的价值定义为这 \(m\) 个元素的和,求价值第 \(k\) 大的子集的价值。
数据范围:
\(1\le n,m,k\le 10^6\)

解法:
尝试去按照上述做法去构造。
先按照从大到小排序。定义一个状态为:\(\{a_1,a_2,\cdots,a_k,a_p,\cdots\}\)
其中一个状态的大小为 m,而下标的范围是 \(1\sim n\)。显然,最大的时候就是选择前面 \(m\) 个。
对每个状态,我们维护 \(k,p\),表示 \([1,k]\) 都在这个状态内,\(p\) 是第一个不满足前缀都在此状态里的被选择的下标。
考虑从这一个状态转移,只能是走 \(k\) 或者走 \(p\)。如果走了 \(k\),那么以后就不能再走这个 \(p\) 了。
那么能够发现,对于任意一个非初始状态,都有一个唯一确定的前驱,就是第一个不满足条件的 \(p\),让他减一就是他的前驱。
并且能够发现,所有状态一直找前驱一定能找到初始状态。
那么这就是一组合法构造。那么我们直接维护这个过程就可以了。
用堆去维护,当前总和,\(k\)\(p\),以及当前的 \(p\) 能够向右移动的最大限度 \(R\)。每个状态最多只有两个后继。

偏序问题

对于一维的偏序问题,可以直接用排序或者树状数组来解决。
对于二维的偏序问题,一般使用树套树/cdq套排序或者其他数据结构。
对于三维的,就要用到cdq套树套树这种东西或者cdq套cdq套树这种科技了。

但是对于更高维度的偏序,容易发现使用这个东西会极为麻烦,每次硬往上叠log,复杂度和常数以及代码难度令人十分的不满意。
所以对于这种问题,我们一般是用bitset去解决。

其实就是按照每一个维度排完序之后,一位一位的加,当前的就是这一位上的位置与上面的所有位置。
询问就是把所有维度的信息 & 一下就可以了。
这样子对时间和空间的复杂度都是 \(O(\frac{n^2}{\omega})\)。几个维度就带几倍的常数。
对于某些空间卡的比较紧的题,可以考虑时间换空间,对每一维排序后分块,对每一块的前缀维护。这样空间复杂度可以优化到 \(O(\frac{n\sqrt n}{\omega})\)

@uoj710 【北大集训2021】魔塔 OL
题目大意:
打怪兽!
初始时刻没有怪兽,有 q 个事件。事件分为三种情况:
第一种,在 (x,y,z) 的位置上出现一个血量为 a,杀死之后血量可以恢复 b 的怪兽;
第二种,将第 k 个出现的怪兽下架;
第三种,询问要杀死满足 \(x\le g,y\le l,z\le d\) 的所有怪兽一开始至少需要的血量。要求不能有任意一个时刻血量小于等于 0。
每次询问不会真正杀死怪兽。
数据范围:
\(1\le q\le 150000,1\le x,y,z,g,l,d\le 10000,0\le a,b\le 10^9\)
怪兽总数不超过 \(50000\),询问数量不超过 \(50000\)

解法:
解法是暴力!
好吧,正经分析一下。
先考虑询问,假设我们已经知道了所有要打的怪兽,考虑贪心策略。
首先我们把 \(a\le b\) 的和 \(a>b\) 的分成两类。
一个比较显然的地方是,我们一定先杀第一类,再杀第二类,而且对于第一类我们是按照 a 从小到大的顺序杀的。这些都比较容易看出来。
那么对于 \(a>b\) 的我们的贪心顺序是按照 \(b\) 从大到小的顺序杀。证明就是考虑把杀的顺序倒过来,我们杀完最后一只怪兽的时候,考虑杀最后一只怪兽之前。那么我们原本是减去了 a 再加上 b,那么倒过来看就变成了减去 b 加上 a。
那么这样子的话,我们就能把第二类当做第一类的来做了,按照第一类的思路,我们倒过来看的时候要按照 b 从小到大杀。那么正过来看就是要按照 b 从大到小杀。
嗯,这就是第一步的贪心。

考虑将所有的怪物离线下来排序,求一个怪物集合的答案就是求 \(\max\limits_{i=1}^n(a_i+\sum\limits_{j=1}^{i-1}a_j-b_j)\)
如果说两个怪物集合,满足其中一个的所有元素都在排序后的数组中在另一个集合的任意元素之前。那么考虑合并就是定义一个 \([sum,pre]\)\(sum\) 表示元素的集合之和,\(pre\) 表示前缀最大值,那么答案就是 左边的那个的答案和 \(sum_l+pre_r\) 这俩求一个较大值就可以了。

其实就可以变成数区间内的点了。那么问题就转化成了一个 4 维偏序的问题(还要加上时间维)。高维偏序?
其实可以写一个什么cdq套cdq套cdq、树套树套树套树之类的东西,但是四个log叠上去感觉真的不如暴力,常数太大,还很难写。
高维偏序的一般做法是bitset优化暴力。
当我们用bitset求出怎么找出满足条件的怪兽之后到了要求答案的时候,我们考虑对序列按照 B 的大小分块,对于每一块的 \(2^B\) 种请款预处理出答案,然后询问的时候每块直接合并答案就可以了。
\(B=\log n\) 的时候复杂度为 \(\dfrac{n^2}{\log n}\)

期望问题

随机游走

这是一类基于期望的问题。
在二维平面上每次随机选一个方向走一个单位的长度,那么走 \(n\) 次的期望长度不会超过 \(\sqrt n\)

证明大概用到势能分析。不会证。

luoguP7606 [THUPC2021] 混乱邪恶
题目大意:
自己读题。

解法:
首先我们把这个六个方向的位置改一下,改成普通平面上的 \(x\) 轴(样例),\(y\) 轴(题面)和 \(y=x\)(数据范围)这三条直线。
那么在每个方向的移动就可以表示为 (0,+1),(-1,0),(-1,-1),(0,-1),(+1,0),(+1,+1),这六个按照输入顺序给入。
考虑朴素dp。我们设 \(f[i][j][k][a][b]\) 为用完前 i 个idea,此时在位置 \((j,k)\),且LG指数为 \((a,b)\) 的情况是否能达到。
那么直接枚举六个方向转移就可以了。时间复杂度:\(O(n^3p^2)\)
如果用bitset去优化这个东西能达到 \(O(\frac{n^3p^2}{\omega})\)。过不去。就差一点。
我们看这个问题到底是啥。就是在一个平面上走来走去,求最后在原点位置能够存在一种情况。
这就是在平面上游走啊。
那和随机游走差在哪了呢?嗯……相信自己,大声说出你的答案!
就差在随机上了啊QwQ
所以在开始的时候给 \(n\) 个idea随机一下就好啦!诶诶诶?????
这样子期望移动的步数就是 \(\sqrt n\) 了。
那么我们把之前的 \(j,k\) 两维变成根号范围内的枚举就好了。
然后时间复杂度就变成了:\(O(n^2p^2)\),然后再加个bitset卡卡,就是:\(O(\frac{n^2p^2}{\omega})\) 了。就能过了呀QwQ.

posted @ 2022-04-11 16:02  灵华  阅读(20)  评论(0)    收藏  举报