做题记录 #1
[AGC049E] Increment Decrement
学习了 \(O(n^4 K)\) 做法和 \(O(n^3 K)\) 做法。
我们对于每一个 \(B_i = [A_i \ge x]\) 考虑把 \(B\) 清空的代价,可以证明把每一层的贡献加起来即为答案。
我们考虑证明每一种操作序列都可以通过调整使得让每一个操作都属于仅一层,且每一层的操作合法。先把区间操作全部放到单点操作前面,不难发现单点操作的规约是容易的。考虑将区间操作转换为在差分数组上面操作,然后在差分数组上面将 \(x\) 视为 \(x\) 个左括号,\(-x\) 视为 \(x\) 个右括号,转换后的括号串一定是合法括号串。然后进行括号匹配,将匹配上的一对括号视为对这个区间的一次操作。容易发现括号匹配转换出来的操作次数和原操作次数相等,且一定可以把括号匹配转换出来的操作扔到一层上考虑。至此我们简要证明了为什么可以分层考虑。
然后不难发现本质不同的 \(B\) 序列只会有 \(O(nK)\) 种,那么我们可以枚举当前层 \(x\) 然后算贡献。
设 \(f_i\) 为已经使得 \(B_{1:i}\) 已经操作好的次数,分成区间操作和单点操作两种转移,具体的就是 \(f_i = \min \{ f_{i-1}+[b_i = 1],f_k + s_i - s_k + C \}\),其中 \(s_i\) 表示前缀 0 个数。那么我们考虑直接维护 \(mn = \min(f_k + s_i - s_k)\),每次就是让 \(mn \leftarrow \min(mn+[b_i = 0],f_i)\)。然后可以做 \(O(1)\) 转移。
考虑扔到变化上面的怎么求,设 \(dp_{i,j,k}\) 为 \(f_i=j,mn=k\) 的方案数,然后算一下有多少种方案使得 \(b_i = 0\),可以做到 \(O(1)\) 转移。因为答案肯定不会超过 \(n\) 次单点操作,所以状态数是 \(O(n^3)\) 的。
对于 \(O(nK)\) 层每一层复杂度为 \(O(n^3) - O(1)\),总复杂度 \(O(n^4K)\)。
重新转化一下题意,维护一个初始全为 0 的序列 \(C\),通过若干次操作使得 \(C=A\)。
我们还是先把区间操作全部放到单点操作前面,对于只进行区间操作后的序列 \(C\),答案为 \(cost + \sum |a_i - c_i|\),其中 \(cost\) 为所有区间操作的代价。考虑那个差分加括号串转换,发现区间操作次数即为左括号个数,即差分 \(>0\) 的所有值总和。设 \(f_{i,j}\) 为 \(c_i=j\) 并且处理了 \([1,i]\) 的所有数的最小代价,有转移 \(f_{i,j} = |a_i - j| + \min_k(f_{i-1,k} + C \times \max(j-k,0))\)。
发现 \(f_i\) 是下凸函数,考虑 slope trick 优化转移。每次先 \(f_{i-1,j} \leftarrow \min_{k}(f_{i-1,k} + C \times \max(j-k,0))\),然后 \(f_{i,j} = f_{i-1,j} + |a_i-j|\)。简单分析可以发现前面的式子保证了斜率值域为 \([0,C]\),再加上一个绝对值函数所以 \(f_i\) 的斜率值域为 \([-1,C+1]\)。前面的式子可以看作将斜率 \(-1 \rightarrow 0,C+1 \rightarrow C\),那么要是维护出 \(C+2\) 个转点就可以每次拍平第一个和最后一个转点,然后再加入两个下标为 \(a_i\) 的转点表示 \(a_i\) 这个地方斜率变化量为 \(2\),我们就可以用 multiset 之类的实现对于单个 \(A\) 序列 \(O(n \log n)\) 求出答案。
初始状态即为 \(f_{0,i} = C \times i\),所以最开始你要往 \(S\) 塞入 \(C+2\) 个 \(0\)。
考虑算答案。如果你得出来了最终的转点集合 \(S\),那么其中 \(-1 \rightarrow 0\) 的转点位置即为 \(S\) 最小值,我们记为 \(p_n\)。那么最终答案即为 \(f_{n,0} - p_n\)。考虑怎么算 \(f_{n,0}\),它是上一段的 \(-1 \rightarrow 0\) 的转点拍平后再加上绝对值 \(|a_n - 0|\) 得到的结果,所以 \(f_{n,0} = a_n + (f_{n-1,0} - p_{n-1})\),通过归纳可以得到最终答案的形式可以写为 \(\sum\limits_{i=1}^n (a_i - p_i)\),这个在扫一遍的时候同时处理下就可以了。
考虑原问题,现在 \(A\) 序列会变化。但是最终答案的形式好像挺简洁,可以拆贡献先对所有 \(A\) 求和再减去所有 \(p_i\) 就可以了。发现维护出来一个 \(p_i = x\) 的方案数比较困难,考虑维护出 \(p_i \le x\) 的方案数。
枚举 \(x\),设 \(dp_{i,j}\) 为维护到了 \(i\),当前集合中有 \(j\) 个数 \(\le x\) 的方案数。计算删掉最小值和最大值后还剩几个数 \(\le x\) 和当前位置 \(\le x\) 的数的个数后,容易做到 \(O(1)\) 转移。初始状态即为 \(dp_{0,C+2} = 1\)。
容易发现集合中只会出现 \(A\) 中的数,所以 \(x\) 的枚举量是 \(O(nK)\) 的,dp 复杂度为 \(O(n^2) - O(1)\),总复杂度为 \(O(n^3K)\)。
CF2030G2 The Destruction of the Universe (Hard Version)
Eazy Ver. \(n \le 5000\)。
首先考虑对于一个给定的集合如何计算答案。观察答案从 \(x \rightarrow x+1\) 变化了什么,右端点在 \(x\) 左边的线段的次数会 +1,左端点在 \(x\) 右边的次数会 -1。
下面我们都用 \(R\) 表示小于等于某个位置的右端点个数,\(L\) 表示严格大于某个位置的左端点个数。
如果找到一个交界点 \(x\),满足在 \(x-1\) 的时候 \(L < R\),在 \(x\) 的时候 \(L \ge R\),那么在 \(x\) 这个地方可以取到答案。进一步的,如果在离散化的时候将在同一个下标的所有端点的下标区分开,那么就可以找到一个 \(x\) 满足 \(L = R\)。
发现当 \(L = R\) 的时候总距离可以看作 \(\sum_{l_i > x} l_i - \sum_{r_i \le x} r_i\),考虑在 \(x\) 这个位置算贡献。具体的,我们对于每一个 \(l_i > x\) 钦定它选中,那么如果 \(cnt1\) 为 \(r_i \le x\) 的 \(r_i\) 个数,\(cnt2\) 为 \(l_i > x\) 的 \(l_i\) 个数,那么对于每一个 \(L (= R)\),都有贡献 \(\dbinom{cnt1}{R} \dbinom{cnt2-1}{L-1} \times l_i\)。对于每一个 \(r_i \le x\) 类似的考虑,有贡献 \(-\dbinom{cnt1-1}{R-1}\dbinom{cnt2}{L} \times r_i\),那么在 \(x\) 这个地方对于答案的贡献即:
交换求和顺序,发现可以范德蒙德卷积优化式子,即:
遍历的同时处理两个前后缀和即可。
但是这样会算重,具体的对于一个集合可能有多个位置满足 \(L=R\) 从而计算多次贡献,考虑只算最左边的位置。容易发现这样的位置一定是一个区间的端点,可以证明仅计算满足当前的 \(x\) 是集合中一个区间的端点的情况就不会算重。证明如下:如果当前的 \(x\) 是某个区间的左端点 \(l_i\),那么在 \(x-1\) 的时候 \(l_i\) 满足 \(l_i > x-1\),从而导致 \(L>R\);如果当前的 \(x\) 是某个区间的右端点 \(r_i\),那么在 \(x-1\) 的时候 \(r_i\) 将不再满足 \(r_i \le x-1\),从而导致 \(L>R\)。于是我们证明了 \(x\) 是极左的。
因为已经把端点拆开考虑了,所以当前的 \(x\) 必定只会是一个区间的端点。我们只需要正常算,再去掉当前区间的贡献就可以了,注意此时 \(n \rightarrow n-1\)。
总时间复杂度 \(O(n \log n)\),瓶颈是离散化。
P11118 [ROI 2024] 无人机比赛 (Day 2)
模拟赛赛时只会 \(O(n^2 \log m) + \text{eps}\) 分。没有观察到前缀最大值只有 \(O(\sqrt{S})\) 个。
首先观察发现如果有 \(t_i < t_j \or (t_i = t_j \and i < j)\),那么 \(i\) 胜利时刻一定在 \(j\) 前面。考虑 \(i\) 总共死了多少次,发现 \(j\) 对 \(i\) 产生的贡献是 \(j\) 赢了多少次。发现 \(i\) 走的时刻 \(j\) 会卡在一个位置不动,分析一下这个性质。
对 \(s\) 做差分是必要的。进一步观察,发现会被卡住的位置只可能是 \(s\) 的严格前缀最大值。而且一个人如果在一次比赛赢了,那么他会一直被选中直到碰上下一个严格前缀最大值或者完赛。注意到 \(s_n \le 1.5 \times 10^5\),所以前缀最大值个数为 \(O(\sqrt S)\)。那么我们可以把 \(s\) 划分成根号段,并记录每一段的长度。具体的,我们记录严格前缀最大值序列 \(s_1,s_2,\dots,s_k\),以及它作为前缀最大值的区间长度 \(l_1,l_2,\dots,l_k\)。
我们不再局限于两人之间的完赛顺序关系(模拟赛考场上卡在这里了!),考虑分析出答案的式子。一个人被选中完赛时的单次用时 \(s_k \times t_i\) 一定是他单次用时最大的一次,所以当他完赛时所有优于这个时间的段全部都被记录过了,也就是这个人完赛前死的次数。那么我们可以推出式子:
其中 \(ans_L\) 表示 \([1,L]\) 的答案。中间艾弗森括号的条件应该还有一个编号相等时咋办,这里忽略。
有点难求这个东西,我们考虑计算 \(ans_L - ans_{L-1}\)。我们枚举 \(i=L/j=L\) 作为贡献。具体的,如果现在要从 \([1,i-1]\) 扩展到 \([1,i]\),答案分为两种贡献:
发现等同于维护两个集合进行 \(n\) 轮操作,第一个集合中每轮先查询 \(1\) 次键值小于等于一个数的权值之和,然后加入 \(O(\sqrt S)\) 个点;第二个集合中每轮先查询 \(O(\sqrt S)\) 次键值大于一个数的点的数量,然后插入 \(1\) 个点。
对值域分块,可以分别使用查询 \(O(\sqrt n)\) 修改 \(O(1)\) 和交换复杂度的分块维护。但是值域可能是 \(O(n \sqrt n)\) 范围,我们仅考虑询问边界把值域变成 \(O(n)\)。具体的,我们可以把条件 \(t_j \times s_{id} \le t_i \times s_k\) 改写成 \(\lfloor \frac{t_j \times s_{id}}{s_k} \rfloor \le t_i\),把每次加的点放到它在 \(t\) 中的 lower_bound 上即可。
直接拿 lower_bound 实现复杂度带 log,但是可以通过将 \(t_i\) 排好序以后用指针维护(排序以后 \(j\) 和 \(i\) 满足单调性),对每一个 \(id\) 都预处理 \(j \in [1,n]\) 的答案,复杂度是 \(O(n \sqrt n )\)。对于第二个条件可以类似的转换成 upper_bound,可以看 lower_bound 的位置上两个值是否相等,如果相等就再跳一个,可以不用额外预处理,省常数。
所以我们就在 \(O(n (\sqrt n + \sqrt S))\) 的复杂度内解决了此题。略有卡常。
[ARC277D] Median of Binary Strings
link,写这一段的时候洛谷还没搬。
感觉这个 01 众数性质非常牛。
考虑一个 01 串能被表示出来的条件。对于询问串 \(T\),对于每一个 \(i \in [1,M]\),都要保证存在一个原串 \(S\) 满足 \(S_i = T_i\),因为我们无法凭空变出来一个颜色。
然后是这个题的关键结论,我们可以证明下面这个条件对于 01 串可以被表示出来是充要的:对于任意的 \(i,j \in [1,M]\),都要保证存在一个原串 \(S\) 满足 \(S_i=T_i,S_j=T_j\)。
必要性证明:仅对 \(i,j\) 这两位考虑,如果要保证操作结果为 \(T_i T_j\),那么 \(S1,S2,S3\) 中满足 \(S_i = T_i\) 的串和满足 \(S_j = T_j\) 的串分别至少要有两个。根据抽屉原理,\(2+2>3\),所以至少存在一个串满足 \(S_i=T_i,S_j=T_j\)。而对于这个串是如何形成的类似地去考虑,最终可以推到存在一个原串满足这个条件。
存在性证明:我们考虑依次固定前缀。长度 \(\le 2\) 的前缀容易得到,对于前缀 abc...,可以通过操作 ab.... a.c... .bc... 来得到。长度 \(l > 3\) 的可以先把固定好前 \(l-3\) 位的串拿出来,再通过 \(l=3\) 的情况操作。
我们可以在 \(O(NM^2)\) 的复杂度内预处理出是否有 \(S_i=x,S_j=y\) 的串。对于询问,我们可以在 \(O(QM^2)\) 的复杂度内判断所有条件。
[AGC038F] Two Permutations
模拟赛做了一整场 T4 建模没建明白的来了。
我们定义 \(A_i = i / B_i = i\) 为 \(i\) 被排序。首先研究什么样的位置可以被排序,发现每次一定是选择一个置换环上所有位置全部排序,原因是你要拿回自己的值。然后显然有一个 \(O(2^n n)\) 的做法是枚举 \(P\) 每一个置换环是否排序,然后再 \(O(n)\) 找到 \(Q\) 在此时的最优解。
发现你要对每一个置换环分配一个 01 值,然后有位置重复的置换环之间会有限制。考虑网络流建模。具体的,设 \(P\) 中排序的置换环集合为 \(S_P\),它的补集为 \(T_P\),与之对应的定义有 \(S_Q,T_Q\)。把两个置换环的贡献拆开,看成每一个位置 \(i\) 的贡献,并提前记录每一个位置所属的置换环编号 \(p_i,q_i\),有下面几种情况:
- \(S_i = T_i = i\),无论如何都有 \(A_i = B_i\)。
- \(S_i = i,T_i \ne i\),当且仅当 \(q_i \in S_Q\) 的时候有 \(A_i = B_i\)。
- \(S_i \ne i,T_i = i\),当且仅当 \(p_i \in S_P\) 的时候有 \(A_i = B_i\)。
- \(S_i,T_i \ne i,S_i \neq T_i\),当且仅当 \(p_i \in S_P,q_i \in S_Q\) 的时候有 \(A_i = B_i\)。
- \(S_i,T_i \ne i,S_i = T_i\),当且仅当 \(p_i \in S_P,q_i \in S_Q\) 和 \(p_i \in T_P,q_i \in T_Q\) 的时候有 \(A_i = B_i\)。
发现还是不好做,因为我们有四个集合。
发现我们只关心 \(S_P \cup S_Q\) 和 \(T_P \cup T_Q\),所以我们把 \(S_P,T_Q\) 合并为 \(S\),\(T_P,S_Q\) 合并为 \(T\),条件可以转换为:
- \(S_i = T_i = i\),无论如何都有 \(A_i = B_i\)。
- \(S_i = i,T_i \ne i\),当且仅当 \(q_i \in T\) 的时候有 \(A_i = B_i\)。
- \(S_i \ne i,T_i = i\),当且仅当 \(p_i \in S\) 的时候有 \(A_i = B_i\)。
- \(S_i,T_i \ne i,S_i \neq T_i\),当且仅当 \(p_i \in S,q_i \in T\) 的时候有 \(A_i = B_i\)。
- \(S_i,T_i \ne i,S_i = T_i\),当且仅当 \(p_i \in S,q_i \in T\) 和 \(p_i \in T,q_i \in S\) 的时候有 \(A_i = B_i\)。
这启示我们可以使用网络流,可以把上面的问题转换为最小割模型。建立源点和汇点 \(S,T\),割完以后和 \(S\) 连通就说明 \(x \in S\),否则 \(x \in T\)。
然后根据割的性质将上面的四个条件转换成边,总共有 \(O(n)\) 个点 \(O(n)\) 条容量为 \(1\) 的边,而且还是二分图,Dinic 跑最大流的时间复杂度是 \(O(n \sqrt n)\)。
QOJ #8351. Ruin the legend
由于有数互不相等,所以我们把原序列划分成若干个极长的等差段,实现可以用 map/unordered_map 之类的东西存,令段数为 \(m\)。考虑容斥计算答案,钦定最终排列的 \(k\) 个位置满足 \(|a_i -a_{i-1}| = k\),设 \(f_{i,j}\) 表示前 \(i\) 个极长段划分成了 \(j\) 段的方案数,那么我们也就要算 \(\sum_{i \ge 0} (-1)^i f_{m,n-i} (n-i)!\)。
考虑转移,枚举当前段被划分成的段数 \(d\),发现转移系数(当前划分方案数)仅和当前段长度有关。简单组合数处理不了,因为如果一个小段的长度 \(\ge 2\) 的话可以翻转。考虑 dp 求解把长度为 \(l\) 的段划分成 \(d\) 段的方案数 \(g_{l,d}\),可以递推求解:\(g_{i,d} = 2\sum_{j<i} g_{j,d-1} - g_{i-1,d-1}\),简单前缀和优化 dp 即可做到 \(O(1)\) 转移,所以求解 \(g\) 的复杂度为 \(O(n^2) - O(1)\) 即 \(O(n^2)\)。
考虑求解 \(f\),枚举当前段被划分成的段数 \(d\),有式子 \(f_{i,j} = \sum_{d} f_{i-1,j-d} \times g_{l_i,d}\)。\(d\) 不会大于 \(l_i\),有 \(\sum d_i = n\),所以对于同一个 \(j\) 而言每一个 \(i\) 的转移总次数是 \(O(n)\) 的,总时间复杂度 \(O(n^2)\)。
至此我们在时间空间复杂度 \(O(n^2)\) 的复杂度内解决此题。
P13495 【MX-X14-T5】魔法卷轴
这题神了,两个部分都非常牛。
由于这个题是关于每行每列的限制,所以考虑把 \(n\) 行看作左部点,\(m\) 列看作右部点,格子内填的数看作左右之间连的边,可以把网格图转换成二分图。(听说是 trick?我咋不会?)
边权为 0/1,考虑每个点最终的点权 \(w_i = 1\),然后一条一条地删边,删边的时候把两端点异或上当前的边权,最终删完以后所有点权应当为 \(w_i = 0\)。初始有些边已经被删掉了,把这些边的贡献提前算好,就不用额外处理了。
考虑对每个连通块分配点权的方案数。我们先拎出来一颗生成树,考虑如果只有生成树上的边怎么做。发现确定根节点后,可以从叶子开始一层一层地递归确定边权。具体的,每个叶子连向父亲的边的边权是叶子目前点权,然后可以删掉叶子和那条边。所以如果只看生成树那么方案就是固定的。特别的,我们也可以发现,无解的情况当且仅当 \(\bigoplus\limits_{i \in V} w_i = 1\)。
考虑加入非树边。对于一个存在解的生成树,我们惊奇地发现非树边可以随便赋值,对点权的改变不会影响生成树的有解。具体来说,如果一条非树边 \(w=0\) 就无事发生,否则就是给两点间路径全部异或上 1。所以,对于一个连通块,它的答案是 \(2^{|E|-|V|+1}\)。
总的答案即为 \(2^{nm-n-m-k+cnt}\),其中 \(cnt\) 为建出来的二分图的连通块个数。
现在考虑维护连通块个数和每个连通块的有解性,考虑并查集。边数是 \(O(nm)\) 的,肯定不能暴力连边。但是我们注意每个左端点会向右端点的若干个区间连边,而这个相当于让右端点的这个区间 \([l,r]\) 合并起来,再向 \(l\) 连边,这样的边数等于区间数。分析一下断点数,发现区间数是 \(O(n+k)\)。所以我们可以把 \(x \rightarrow [l,r]\) 看作 \(x \rightarrow l\) 和 \(l \rightarrow l+1 \rightarrow \dots \rightarrow r\)。前者可以直接做,后者可以维护一个类似于差分的东西。由于我们只关注连通性,所以这么做是可以接受的。
视 \(n,m,k\) 同阶、并查集复杂度为 \(O(\log)\),时间复杂度是 \(O(n\log n)\)。

浙公网安备 33010602011771号