模拟赛记录

2023.11.22

计数场。

\(100+0+0+0=100\)

  • C0392 B 【1109 B组】预处理器

题意:求有多少个长度为 \(n\) 的数组 \(a\) 满足以下条件。

  • 条件一:\(l_{i} \le a_{i} \le r_{i}\)

  • 条件二:\(a_{i}\)\(2\) 等于 \(p_{i}\)

  • 条件三:\(s \le \sum a_{i} \le t\)

求答案模 \(mod\) 的值,\(mod\) 不一定是一个质数。

数据范围:\(n \le 13\)

又积累到一个新的 Trick:看到这种 \(2^{n}\) 能过的数据范围,又是计数,可以往容斥方面想。

显然,\(a_{i}=2\times x_{i}+p_{i}\),那么将构造数组 \(a\) 转化为构造数组 \(x\),这样可以消除条件二。

发现可以把条件三转化为 \(a_{i} \le t\) 的答案减去 \(a_{i} \le s-1\) 的答案,这样更好统计答案。然后考虑转化条件一,把上界 \(r_{i}\) 去掉,做容斥即可。具体来讲,设 \(S\) 为一个 \(n\) 位的二进制串,如果第 \(i\)\(1\),就需要满足 \(a_{i} \ge r_{i}+1\),为 \(0\) 则需要满足 \(a_{i} \ge l_{i}\)。最后的答案就是 \(S\)\(1\) 的数量为偶数时的答案减去 \(S\)\(1\) 的数量为奇数时的答案(容斥)。

那对于每一个 \(S\),怎么计算答案呢?首先可以将 \(a_{i}\) 的和的上界 \(tot\) 减去每一个 \(a_{i}\) 的下界,因为每一个 \(a_{i}\) 都至少有这么多。然后问题就转化成了有 \(n\) 个相同盒子和 \(tot\) 个相同球,每个盒子可以放任意个球(可以为零),有多少种本质不同的放法,显然插板法直接做即可。

注意:\(mod\) 不是质数,没有逆元,所以在算组合数的时候要先把分母算出来,然后和分子一一约分,最后将分子相乘即可。

2023.11.24

\(100+40+55+0=195\)

  • C0396 【1124 C组】模拟测试 ksum

题意:你有一个长度为 \(n\) 的正整数数组,第 \(i\) 个数为 \(a_{i}\)。你求出了这个数组的所有子段和,并将这些数降序排序,请输出前 \(k\) 个数。

首先预处理出 \(a\) 数组为前缀和,\(b\) 数组为后缀和的逆序,将题意转化为求 \(a_{i}+b_{j}\) 的前 \(k\) 小。

做法一 (赛时做法):首先枚举 \(a\) 数组,对于每一个 \(a_{i}\),以及范围在 \(1\)\(k/i\) 中的所有 \(j\) ,把 \(a_{i}+b_{j}\) 放入优先队列,输出前 \(k\) 个即可。为什么只需要枚举范围在 \(1\)\(k/i\) 中的所有 \(j\)?因为 \(a\) 数组中的 \(1\)\(n\)\(b\) 数组的 \(1\)\(k/i\) 已经组成了 \(k\) 个数了,后面的数肯定比它们小,时间复杂度 \(O(n \log^{2} n)\)

做法二:维护一个优先队列存当前 \(a_{i}+b_{j}\) 的最小值,然后将它计入答案,并且将 \(a_{i+1}+b_{j}\)\(a_{i}+b_{j+1}\) 加入队列,因为 \(i\)\(j\) 前面的数一定已经计入答案了,而后面最小的一定是 \(a_{i+1}+b_{j}\)\(a_{i}+b_{j+1}\)。时间复杂度 \(O(n \log n)\)

  • C0396 【1124 C组】模拟测试 label

题意:有一棵含有 \(n\) 个节点的树,你现在要给每个节点赋一个 \(1\)\(m\) 之间的整数权值,并保证有边直接相连的两个节点的权值之差的绝对值大于等于 \(k\),你想知道有多少种不同的赋值方案,两个方案不同当且仅当个点中至少有一个点的权值被赋值的不同,输出答案对 \(1e9+7\) 取模后的值。

首先设 \(dp_{i,j}\) 表示点 \(i\) 的值为 \(j\)\(i\) 的子树中的总方案数,则答案为 \(\sum_{i=1}^{m} dp_{1,i}\)

那么 dp 转移方程为 \(dp_{u,i}=\sum_{j=i-k}^{i+k} dp_{v,j}\),但是这是 \(O(nm)\) 的,只有 \(40\) 分。

我们可以发现,其实每一个 \(u\)\(m\) 个 dp 值的中间很长一段都是相同的,准确来说,应该只有前后的 \((n-1)k\) 个值是不同的 (每往上转移一次两边会多 \(k\) 个值不同,一共最多 \(n-1\) 条边),于是我们可以把中间这一段相同的值特殊记录一下,然后前后的 \((n-1)k\) 个不同的 dp 值直接暴力转移即可,时间复杂度 \(O(n^{2}k)\)

  • C0396 【1124 C组】模拟测试 最短路

题意:有一个 \(n\) 个点 \(m\) 条边的无向图,给定图中的 \(k\) 个点,求一条经过这 \(k\) 个点的路径,保证这条路径是某两个点之间的最短路,并且让长度尽量小,数据保证有解。

很巧妙。

因为要求路径必须是一条最短路并且长度最小,所以不妨直接让两个端点在这 \(k\) 个点中,这样一定更优。然后又因为这条路径是两个点的最短路,所以一定是一次性穿过这 \(k\) 个点的一条链。又因为数据保证有解,于是我们可以用类似于找树的直径的方法,先随便拎 \(k\) 的点中的任意一个点出来,跑到这 \(k\) 个点中离它最远的点 \(u\),在从这个点 \(u\) 开始跑到离 \(u\) 最远的点,这个距离就是答案。

  • C0396 【1124 C组】模拟测试 square

题意:给定一个 \(n\times m\) 的地图 \(a\)\(a_{i}\)\(0\)\(1\)。有 \(t\) 次询问,每次询问给定一个矩形,求出这个矩形中最大的由 \(1\) 构成的正方形的边长是多少。

首先考虑预处理出 \(d_{i,j}\) 表示以 \((i,j)\) 为左上角的最大正方形边长是多少。

对于每一次询问可以二分一个 \(mid\),判断 \(mid\) 是否可行就相当于是查询 \(d\) 的一个二维区间中的最大值,如果这个最大值大于等于 \(mid\) 说明可行,否则不可行,注意如果这个二维区间的长宽的最小值比 \(mid\) 小的话,那么肯定不可行。而二维区间 \(\max\) 直接用二维 ST 表维护即可,时间复杂度 \(O(n^{2} \log ^{2}n+t \log n)\)

2023.11.27

\(100+0+0+10=110\)

  • C0398 【1127 B组】模拟测试 路径

题意:给定一棵 \(n\) 个点的树,对于每一个 \(k=0\)\(n\),求出满足 \(mex(S(u,v))=k\) 的数量,其中 \(mex(S(u,v))\) 表示 \(u \to v\) 的路径的点的编号的 \(mex\) 值。

很简单。当 \(mex=k\) 时当且仅当 \(0\)\(k-1\) 编号的点都出现过,但是编号为 \(k\) 的点没有出现。容斥一下即可,转化为求有多少条路径满足:经过了编号为 \(1\)\(k\) 中的所有点。

我们只需要从小到大枚举 \(k\),对于当前枚举到的 \(i\),分类讨论一下然后把它加入路径即可,时间复杂度 \(O(n\log n)\)

  • C0398 【1127 B组】模拟测试 序列

题意:给定一个长度为 \(n\) 的序列,求最少能将这个序列分成多少段使得任意一段中不存在两个数的积为完全平方数。你还可以将其中 \(k(k \le 20)\) 个数修改为任意的值。

一个小 Trick:如果两个数乘起来为平方数,可以先将每个数的平方因子除掉,然后这两个数必然相等。于是这道题被转化为了一个区间不能有相等的值。

首先考虑没有修改的情况怎么做,设 \(pos_{a_{i}}\) 表示上一个 \(a_{i}\) 出现在哪里(也就是最远可以满足条件的位置),那么有 dp 转移方程 \(dp_{i}=dp_{pos_{a_{i}}}+1\),时间复杂度 \(O(n)\)

然后考虑有修改操作的情况,我们可以稍微改变一下 \(pos\) 数组的定义,改为 \(pos_{i,j}\) 表示当前点为 \(i\),修改 \(j\) 次后可以到达的最远的满足条件的位置。那么怎么转移呢?\(pos_{i,j}=\min(pos_{i-1,j-1},last+1)\)\(last\) 表示上一个 \(a_{i}\) 出现的位置。因为 \(pos_{i-1,j-1}\)\(last+1\) 这两个位置都有可能是修改次数的分界点,看哪个更小即可。然后就可以用 \(pos\) 数组来 dp 转移了:\(dp_{i,j}=dp_{pos_{i,k},j-k}+1\)。最终的答案就是 \(dp_{n,k}\),时间复杂度 \(O(nk^{2})\)

注意分解质因数的时候可以先将质数线性筛出来,这样效率更高。

  • C0398 【1127 B组】模拟测试 游戏

题意:有一个 \(n\) 个点的环,以及两个人。每个人可以向环中任意一个位置放置一个 \(A\) 或者 \(B\),但是相邻的位置不能相同,不能行动者输。问最终的局面有多少种。

一个结论是:后手必胜。

证明:最终肯定不可能出现两个连续的空格,否则一定可以在其中一个上填 \(A\)\(B\)。所以最多只有一个空格,而这个空格两边填的字母一定是不一样的,否则一定可以在这填一个和它两边不一样的字母。所以如果把所有空格去掉之后,一定是 \(ABABAB\dots\)(\(k\)\(AB\)) 的形式。步数一定为偶数,所以后手必胜。

问题转化成了有多少种在环上放空格的方式,使得每两个空格都不相邻。可以先断环为链,然后分类讨论,假设当前要选 \(i\) 个空格(必须满足 \(n-i\) 为偶数)。如果链的第一个位置为空格,则后面 \(n-1\) 个要选出 \(i-1\) 个空格且首尾不能选,方案数 \(C^{i-1}_{n-i-1}\)(插空法);如果链的第一个位置不为空格,则后面 \(n-1\) 个要选出 \(i\) 个空格且首尾可以选,方案数 \(C^{i}_{n-i}\)(插空法)。注意还要乘以 \(2\times(n-i)!\)\(2\) 表示可以从 \(A\) 开始也可以从 \(B\) 开始,\((n-i)!\) 表示每个人每次选择位置的顺序。

枚举 \(i\),累计答案即可。

2023.11.29

\(0+40+40+0=80\)

  • C0400 【1129 B组】模拟测试 算面积

题意:有一个 \(n\times m(n,m \le 10^{5})\) 的图,每个点都是一个 \(0\)\(9\) 的数字,但每一行的数都是循环的,循环节为 \(a_{i}(a_{i} \le 100)\)。另外有 \(t\) 个询问,每个询问给出一个矩形,求这个矩形内数的和。(时限 \(4s\))

赛时写出来了,但是因为数组开大 MLE 了。直接二维前缀和时空都是 \(O(nm)\) 的。但是因为循环节很小,所以可以对于每一种循环节做二维前缀和。询问时对于每一种循环节计算答案,先把中间重复的一大段直接加上,然后用预处理出来的 \(sum\) 数组求值即可。时间复杂度 \(O(100\times t\log n)\),常数大,空间复杂度 \(O(100n)\)。如果把询问离线下来,可以做到 \(O(n \times 100^{2})\),常数小。

  • C0400 【1129 B组】模拟测试 猜数

题意:给定一个数 \(z\),求有多少个 \(x\) 满足 \(x+f(x)=z\),其中 \(f(x)\) 表示将 \(x\) 翻转。

奇奇妙妙构造题。

\(z\) 存到 \(b\) 数组(\(n\) 为最高位,\(1\) 为最低位)中,可以发现,如果不进位的话,那么 \(z\) 的每一位一定是左右对称的(\(b_{i}=b_{n-i+1}\))。考虑如何求出不进位情况下的 \(b\) 数组。依次从外到内处理 \(z\) 的每一位,假设当前处理到的位置为 \(l\),右边对应的位置为 \(r\)。如果 \(b_{l}=b_{r}\) 则已经合法,直接跳过即可;如果 \(b_{l}>b_{r}\),因为 \(r+1\)\(n\) 已经确定了,所以 \(b_{r}\) 不可能增加了,只能变 \(b_{l}\)。我们可以将 \(b_{l}\) 减去 \(10\),然后将 \(b_{l+1}\) 加一,这样总的值是不会变的(相当于是借了一位);如果 \(b_{l}<b_{r}\) 的话,不可能让 \(b_{l}\) 直接加 \(10\),因为这样一定会大。所以我们可以让 \(r\) 这一位是最终 \(r-1\) 这一位进位得到的。所以 \(b_{r}\)\(1\)\(b_{r-1}\)\(10\)。又因为 \(r-1\) 这一位最终要进位,对称过去 \(l+1\) 也要进位,所以 \(b_{l+1}\)\(10\)\(b_{l+2}\) 减一。

这样就求出 \(b\) 数组了,最终只需要计算总方案数即可。一开始 \(res=1\),如果 \(b_{i}=x_{1}+y_{1}=x_2+y_2=x_3+y_3=\dots=x_k+y_k(0\le x_i,y_i\le 9)\),那么就将 \(res\) 乘上 \(k\) 即可。

注意最开始如果 \(z\) 的最高位为一且最低位为一的话既有可能是两个比 \(z\) 少一位的数加起来也有可能是两个和 \(z\) 位数相同的数加起来。如果最高位为一但是最低位不为一就只有可能是两个比 \(z\) 少一位的数加起来(否则最低位一定为一),如果最高最低为都不为一那么只能是两个和 \(z\) 位数相同的数加起来。

  • C0400 【1129 B组】模拟测试 排序

题意:有一个 \(n\) 个数的序列 \(a\),我们从 \(1\)\(n\) 排序。如果当前排到 \(i\) 号且它不是它后面的所有数的最小值,那么花费 \(1\) 的代价将它放到最后。求结束排序时花费的代价。

我们会发现每一次不会被排到最后的数一定是当前还没有被排序的数中的最大值。所以我们可以从大到小统计每一种数的代价。把一直往后面放数的操作转化成一个指针在环上面移动,且只会走过没有被排序的数,指针移动的步数就是相应的代价。那么对于每一个数 \(i\),指针会移动多少呢?指针的起始位置一定是上次 \(i-1\) 的结束位置,并且这个指针会经过每一个值为 \(i\) 的位置,并且在最后一个值为 \(i\) 的位置停下,这个距离是很好算的。但是中途不会经过已经排好序的数,即大于 \(i\) 的数,还需要减去这一部分的代价,用树状数组维护即可,时间复杂度 \(O(n \log n)\)

2023.12.1

\(90+25+20+5 = 140\)

  • C0402 【1201 B组】模拟测试 串

题意:构造一个长度为 \(n\) 且包含 \(k\)\(1\)\(01\) 串,使得包含奇数个 \(1\) 的区间个数最多。

赛时打表找规律做的,发现前 \(k-1\)\(1\) 全部在左边,剩下的一个 \(1\) 放在剩下所有位置的中间。但是没有特判 \(k=0\) 的情况挂了 \(10\) 分。

讲一下证明。首先算出这个串的前缀异或和数组 \(a\),假设区间 \(l\)\(r\) 有奇数个 \(1\),那么一定有 \(a_{r}+a_{l}=1\)。所以如果 \(a_{0}\)\(a_n\) 一共有 \(x\)\(1\)\((n+x-1)\)\(0\),那么满足条件的区间数为 \(x(n+x-1)\)。用均值不等式求最大值即可。发现构造方法和上述一样。

  • C0402 【1201 B组】模拟测试 黑白树

题意:给定一棵 \(n\) 个点的树,每一个结点都可以是黑色或白色。定义一棵树的价值,为同色点距离的最大值。求出在所有情况下,树的价值之和,对 \(10^9 + 7\) 取模.

很显然,同色点的最大距离一定是树的直径。令直径的两个端点为 \(x,y\),我们从大到小考虑每个距离对应多少种情况,假设答案距离为 \(i\),那么所有到 \(x\) 距离 \(> i\) 的点颜色必须与 \(y\) 一样,所有到 \(y\) 距离 \(> i\) 的点颜色必须和 \(x\) 一样。假设当前的总情况数为 \(tot\),那我们可以利用容斥的原理,假设不满足上述条件(最大距离 \(\le i -1\))的情况数为 \(k\),那么满足条件的情况数就为 \(tot-k\)。那么问题在于如何求 \(k\),其实只要距离 \(x,y\) 的距离等于 \(i\) 的所有点都刚好分别为两个颜色的时候,其它点无论怎么取值,情况都一定不合法,因为最大距离一定小于 \(i\),所以只需要预处理出 \(2^{p}\) 即可。然后每一次要将 \(tot\) 赋值为 \(k\),因为 \(k\) 就是当最远距离为 \(i - 1\) 时的总情况数。 时间复杂度 \(O(n)\)

2023.12.5

\(90+30+0+0=120\)

  • C0429 【1205 B组】模拟测试 猫咪

题意:有 \(n(n \le 10^9)\) 个点,每次可以选择在当前点停下或者向右跳 \(1/2/k(k \le 10^2)\) 个点。初始时可以跳到任何一个点上。求经过的点组成的序列的种数。

场切了,但是细节没处理好挂了 \(10\) 分。感觉对矩阵优化 dp 的理解深刻了不少。

\(dp_{i}\) 表示在 \(i\) 以及 \(i\) 之前的点停下的方案数。那么有一个类似于前缀和的转移:

\(dp_i=dp_{i-1}+(dp_{i-1}+(dp_{i-1}-dp_{i-3})+(dp_{i-k}-dp_{i-k-1}))\)

朴素转移是 \(O(n)\) 的,用矩阵可以优化到 \(O(k^3 \log n)\)

  • C0429 【1205 B组】模拟测试 字符串

题意:给出⼀个长度为 \(n\) 的字符串,你需要求这个字符串所有子序列的价值和。一个字符串的价值指这个字符串的所有的长度⼩于等于 \(\left \lceil \frac{n}{2} \right \rceil\) 的 border 的长度的和(\(n \le 300\))。

看到数据范围,我们可以想到类似于区间 dp 的东西。因为 border 前后完全相同,所以我们设 \(f_{i,j}\) 表示有多少对子序列满足前一段结束于 \(i\),后一段结束于 \(j\),且两段相同。 但是我们发现即使求出了 \(f\) 数组我们也只知道有多少对 border 串而不知道具体的长度。所以我们可以多枚举一个 \(p\) 表示后一段开始的位置为 \(p\),并且设 \(g_{i,j}\) 表示满足条件的子序列的长度之和,这样最终答案就是:\(\sum_i\sum_j g_{i,j} \times 2^{\max(p-i-1,0)}\)。(因为两段中间的位置都可以选或不选)

接下来考虑如何转移,\(f\) 的转移是普通的:\(f_{i,j}=\sum_{k=1}^{i}\sum_{o=i}^{j}f_{k,o}\)。但是如何转移 \(g\)?其实我们会发现,\(g\) 的每一次转移相当于在每一个 border 串的后面再添加了一个字符,所以当前的 \(g\) 就应该等于用来转移的 \(g\) 加上当前有多少 border 串,也就是 \(f\)。于是有:\(g_{i,j}=\sum_{k=1}^{i}\sum_{o=i}^{j}f_{k,o}+g_{k,o}\)。这两个方程朴素转移是 \(O(n^5)\) 的,用二维前缀和可以优化到 \(O(n^3)\)

注意当 border 的长度等于 \(\left \lceil \frac{n}{2} \right \rceil\) 时要加一,因为它自己一个字符可以作为子序列。

2023.12.7

\(100+100+0+6=206\)

  • C0431 【1207 B组】模拟测试 小数点

题意:计算 \(\frac{n}{m}\) 的小数点后第 \(l\)\(r\) 位。\((n,m,l,r \le 10^9,r-l+1 \le 10^5)\)

先将 \(\frac{n}{m}\) 乘上 \(10^{l-1}\),然后对于每一位直接暴力除即可,时间复杂度 \(O(r-l+1)\)

  • C0431 【1207 B组】模拟测试 体力劳动

题意:给定一个长度为 \(n(n \le 10^5)\) 的序列 \(a\) 和一个数 \(x\)。求出一个最小的 \(len\),使得存在一个长度为 \(len\) 的区间满足这个区间进行冒泡排序的交换次数大于 \(x\)

首先冒泡排序的交换次数其实就等价于区间逆序对数。看到求最小值,考虑二分。对于每一个 \(mid\),我们可以从左往右维护一个长度为 \(mid\) 区间,如果有一个长度为 \(mid\) 的区间的逆序对数大于 \(x\),那么 \(mid\) 就是合法长度。现在问题转化成了如何动态维护长度为 \(mid\) 的区间的逆序对数。看到维护逆序对数,容易想到权值树状数组。具体来讲,动态维护一个答案 \(ans\),区间每一次向右移动,都要将 \(ans\) 减去左端点的贡献,加上右端点的贡献。并且在树状数组上也要做相应的修改。时间复杂度 \(O(n \log^2 n)\)

upd:用双指针代替二分答案可以将时间复杂度优化到 \(O(n \log n)\)

  • C0431 【1207 B组】模拟测试 距离之和

题意:给出一颗 \(n\) 个点的无根树,对于每一个点 \(u\),求出最少进行多少次操作才能使得 \(f(u)\) 小于等于任意的 \(f(v)\)\(f(x)\) 指所有点到 \(x\) 点的距离和,一次操作指删一条边,再任意填一条边,保证原图还是一棵树。

要让 \(f(u)\) 是所有点中最小的,当且仅当 \(u\) 为重心,证明可以用反证。于是问题就变成了对于每一个 \(u\),问需要操作多少次才能使得 \(u\) 变为重心。我们先找到当前的重心 \(root\),显然重心的答案为 \(0\)。并且以这个重心为根求出每个点的 \(size\)

前置知识:\(u\) 的最大子树大小小于等于 \(\frac{n}{2}\)\(u\) 为重心的充分必要条件。

先只考虑删边。对于一个点 \(u\),考虑删成什么样子的时候才能使得存在一种连边方式使得 \(u\) 为重心。我们会发现满足上述条件当且仅当:删完边后,不包含 \(u\) 的联通块的大小小于等于 \(\frac{n}{2}\),包含 \(u\) 的联通块中所有子树的大小小于等于 \(\frac{n}{2}\)。所以只需要考虑如何尽可能少地删边,使图满足这个条件即可。

\(b_u\) 表示点 \(u\) 的祖先节点中和 \(root\) 相邻的那一个(形象地说就是 \(u\) 所在子树的顶点)。如果我们想要把 \(u\) 变为重心,那么我们会发现:

  1. 如果删了一条 \(root\) 的其它子树内的边,则将那条边换成那个子树与 \(root\) 相连的边,方案仍然合法。这是因为那个子树之外的点所属连通块不会变大,而那整个子树的大小也不会超过 \(\frac{n}{2}\)

  2. 如果删除了 \(b_u\) 子树内部的边,则将那条边换成 \(b_u\)\(root\) 相连的边,方案仍然合法。这是因为 \(b_u\) 子树之外的点所属连通块不会变大,而 \(b_u\) 这颗子树的大小也不会超过 \(\frac{n}{2}\)

综上,我们先贪心地只删 \(root\) 和某个点之间的边,且贪心地按 \(size\) 从大到小删,只到剩下的联通块的大小 \(\le \frac{n}{2}\) 为止,记录删了多少条边(\(ans\))即可。

但是最终包含 \(u\) 的那一个联通块的大小不一定小于 \(\frac{n}{2}\),所以我们还要考虑包含 \(u\) 的那一个联通块的大小大于 \(\frac{n}{2}\) 的情况。

如果是这种情况,那么最终 \(b_u\)\(root\) 的边一定是留下来的(否则不可能大于 \(\frac{n}{2}\)),那么还需要分类讨论。

  1. 如果 \(b_u\)\(root\) 的边已经被删了,那么我们判断是否可以将这条边连上,是则更新答案。并且可以证明不可能再连其它边了,不然包含 \(u\) 的这个联通块的子树大小就大于 \(\frac{n}{2}\) 了。

  2. 如果 \(b_u\)\(root\) 的边没有被删,那么我们判断是否可以把原来删过的最小的子树连回去,是则更新答案。并且可以证明不可能再连其它边了,不然包含 \(u\) 的这个联通块的子树大小就大于 \(\frac{n}{2}\) 了。

最后会发现一个神奇的结论,最后的答案一定是 {\(0,ans,ans-1\)} 其中之一,且取到 \(ans-1\) 的情况一定是包含 \(u\) 的那一个联通块的大小大于 \(\frac{n}{2}\) 的情况,时间复杂度 \(O(n)\)

2023.12.9

\(70+20+50+100=240\)

  • C0433 【1209 B组】模拟测试 猫猫小游戏

题意:有一个 \(n\times n\) 的网格,每一次操作可以将每一行或每一全部涂上 \(0\)\(1\)。给出一个 \(01\) 矩阵,问最终是否可能将网格涂成这样。

考虑从最终状态入手。要是能涂完当且仅当最终有一行或一列的颜色全部相同,那么我们先把这一行删掉。然后把删完这一行的网格作为最终网格,继续做这种操作即可。最后如果能够全部删完的话就是可行的,否则不可行。注意删去一行的时候也要删掉行内每一个点对它所在列的贡献。时间复杂度 \(O(n^{2})\)

  • C0433 【1209 B组】模拟测试 猫猫的数列

题意:构造一个长度为 \(n\) 的数列 \(a\),满足:\(a_i\le m\),有且仅有一对数相等,前半部分严格单增,后半部分严格单减。求方案数模 \(p\) 的结果。

推式子。首先从 \(m\) 种数中间选出 \(n-1\) 个,有 \(C_m^{n-1}\) 种方案。先暂时不管那一对相等的数,一共 \(n-2\) 个数。以最大的那个数为中心,从大到小依次填数。每个数都可以填在左边或者右边,有 \(2^{n-3}\) 种填法,最后再从 \(n-2\) 种数(不能选最大数,因为要求严格单调)中选出来一个相等的,且这个数的位置固定,所以有 \(n-2\) 种选法。于是总方案数为 \(C_m^{n-1} \times 2^{n-3}\times (n-2)\)

注意模数可能比 \(n\)\(m\) 大,所以处理组合数要用卢卡斯定理解决。

  • C0433 【1209 B组】模拟测试 猫猫与括号

题意:给定一个括号序列,支持修改以及查询是否合法两种操作。

把左括号看成 \(1\),右括号看成 \(-1\)。序列合法等价于没有一个位置的前缀和小于 \(0\)。于是用线段树维护前缀 \(\min\) 即可。

2023.12.13

\(80+0+30+20=130\)

不好评价,一道阅读理解题 + 不确定是不是错题,一道原题,一道析合树。

  • C0435 【1213 B组】模拟测试 文学问题

先用两次将两边的和加到 \(l\),然后两边轮流加 \(3\) 即可。

  • C0435 【1213 B组】模拟测试 数学问题

先用线性筛筛出 \(1\)\(10^7\) 的质数,然后将 \(a\) 数组的数分解质因数。记录是质数或者是质数的次幂的数,以及值为 \(1\) 的数。然后转化为求区间出现次数大于区间长度的一半的数,用莫队或分块或主席树维护。

  • C0435 【1213 B组】模拟测试 地理问题

题意:给定一个 \(n\) 个点 \(m\) 条边的无向图,以及 \(q\) 个限制 \(s,t\)。询问是否有一种给每条边定向的方案,使得每一个 \(s\) 都能走到 \(t\)

判断两点能否到达,想到边双。首先求出图中的所有边双,对于在同一个边双内的点 \(s,t\),一定存在一种定向方式使得 \(s\) 能走到 \(t\),不用管这一类点。然后将原图进行边双缩点形成一棵树。那么此时如果 \(s\) 想要走到 \(t\),那么 \(s \to t\) 的路径上的边的定向方式是唯一的(\(s\)\(lca\) 的边的方向向上,\(lca\)\(t\) 的边的方向向下),于是我们可以树上差分来维护这一信息。最终查看是否有一些边既需要向上又需要向下,如果存在这类边,则是不合法的;否则合法。

2023.12.14

\(100+50+30+20=200\)

  • C0436 【1214 B组】模拟测试 序列函数

题意:定义一个函数 \(f(l,r)=f(a_l \oplus a_{l+1},a_{l+1} \oplus a_{l+2},\dots,a_{r-1} \oplus a_r)\)。特殊的,\(f(l,l)=a_l\)
。有 \(q\) 次询问,每次给定 \(l,r\),求出 \(\max_{l \le i \le j\le r}f(i,j)\)

我们会发现,\(f(l,r)=f(l,r - 1) \oplus f(l + 1,r)\),这个可以递推得到。然后设 \(dp_{i,j}\) 表示 \(\max_{l \le i \le j\le r}f(i,j)\),那么有 \(dp_{i,j}=\max(f_{i,j},\max(dp_{i + 1,j},dp_{i,j 0-1}))\)\(O(n^2)\) 转移即可。

  • C0436 【1214 B组】模拟测试 历史

题意:给定一棵 \(n\) 个点的树。再给定 \(m\) 个树上的点 \(a_i\) 以及一个权值 \(d_i\)。对于每个点 \(u\),求出 \(\sum_{i=1}^{m}\max(0,dis(u,a_i)-d_i)\)

赛时想到正解,但是无脑地认为 \(10^8\) 的数组会 MLE,只有暴力分。

因为 \(d\) 的值域只有 \(1000\),于是设 \(dp_{u,j}\) 表示对点 \(u\) 的贡献为 \(j\) 的点的数量。那么对于 \(u\) 的子树内有 \(dp_{u,j}=\sum dp_{v,j+1}\),对于 \(u\) 的子树外的点换根计算即可,注意还需要减去两次计算时重叠的部分,时空复杂度 \(O(1000n)\)

  • C0436 【1214 B组】模拟测试 附魔

神奇转化 + 构造 + 性质 + dp。

有一个很神奇的转化。我们可以将整个附魔过程看做是一颗树的形式,一次合并为一条边,其中消耗品是子节点,主物品是父节点。对于一个节点 \(i\),它会造成两个贡献:魔咒的消耗 \(depth_i \times a_i\) 和额外消耗 \(2^{d_i}-1\),其中 \(depth\) 表示深度,\(d\) 表示子节点个数。魔咒消耗乘上深度的原因是每一次向上操作的时候都会产生 \(a_i\) 的消耗(\(a_i\) 向上累加),额外消耗为 \(2^{d_i}-1\) 的原因是当前点每和一个子节点操作一次,额外消耗都会乘二。所以题意转化为构造一种树使得总消耗最小,先挖掘一下这棵树的性质:

  • 同深度的点的度数差不超过一,否则可以将度数大的点的一颗子树移到度数小的点上,消耗一定不会变大。

  • 所有子节点的度数都不超过父节点,否则可以将子节点的一颗子树移到父节点上,消耗一定不会变大。

  • \(depth_i<depth_j\),那么 \(a_i>a_j\),否则可以交换这两个点,这样 \(depth_j \times a_j\) 最小。(按 \(a\) 降序排序)

然后就可以 dp 了,设 \(dp_{s,a,b}\) 表示当前层的第一个点编号为 \(s\),这一层一共有 \(a\) 个点,下一层一共有 \(b\) 个点时的最小消耗。先考虑魔咒的消耗,因为 \(depth_i \times a_i\) 不好直接算(因为不知道 \(i\) 的深度),所以可以将 \(a_i\) 拆分到每一个在 \(1\)\(i\) 的路径上的点上,这样每个点的贡献就变为了 \(\sum a_j(j \ge i)\),预处理出后缀和即可。再考虑额外消耗,假设一层有 \(b\) 个点,其中 \(c\) 个点的度数为 \(D\),其它点的度数为 \(D-1\)(性质一),那么消耗为 \(c\times (2^D-1)+(b-c) \times (2^{D-1}-1)\)。但是我们会发现不好直接从父亲节点转移过来(信息不好求),所以可以用刷表法转移。最后的问题就是当前状态可以转移到哪些合法的状态了。枚举现在 \(b\) 这一层的最大度数 \(mxd\),那么必须满足 \(mxd \le \frac{b}{a}\)(性质二),再枚举 \(c\) 表示 \(b\) 这一层有多少个度数为 \(mxd\) 的点,那么其余 \((c-k)\) 个点的度数都为 \(mxd-1\)(性质一)。所以 \(b\) 的下一层的总点数为 \(nxt=mxd \times c+(mxd-1)\times (b-c)\),然后就可以从 \(dp_{s,a,b}\) 转移到 \(dp_{s+a,b,nxt}\) 了。

2023.12.18

\(100+50+20+20=190\)

  • C0439 【1218 B组】模拟测试 铺设积木

题意:给定 \(n\) 个高度 \(h_i\) 和一个数 \(k\),一个操作指将一个区间整体加上或减去一个数、问最少操作多少次才能使相邻两个高度之差的绝对值不超过 \(k\)

因为要求相邻两数差不超过 \(k\),考虑差分。对于差分数组,一次操作相当于在一个位置加一,一个位置减一。那么我们要把大于 \(k\) 的位置减到 \(k\),小于 \(-k\) 的位置加到 \(-k\)。分别计算这两种操作各需要多少次,最终取 \(\max\) 即可。

  • C0439 【1218 B组】模拟测试 开关灯

简化>题意:有 \(n\) 个数,问有多少个数有偶数种质因数。

\(f_i=1/0\) 表示 \(i\) 这个数是否满足条件,可以发现 \(f_i\) 是一个积性函数。那么我们可以用线性筛进行递推。对于一个 \(i\),它的最小质因数为 \(p\)。若 \(p|\frac{i}{p}\) 那么 \(f_i=f_{\frac{i}{p}}\);否则 \(f_i=!f_{\frac{i}{p}}\)。时间复杂度 \(O(n)\)。若追求更优的时间复杂度的话好像要用 Min_25 筛。

  • C0439 【1218 B组】模拟测试 历史

非常非常典的点分树板子,但是赛时忘记怎么写了。

讲一下点分树的一般思路:先把每层的重心找到,对于每一个重心,维护两个信息。

  • 当前重心所在连通块内的所有点到重心的贡献。

  • 当前重心所在连通块内的所有点到当前重心的父亲重心(上一层的重心)的贡献。

然后算贡献的时候要容斥一下。对于当前重心,需要减去儿子重心所在连通块对当前重心的贡献(因为这些贡献在算儿子重心时已经算过了)。代码:res = sum[i] - sum[son[i]];

如果要支持修改操作的话可能会套上数据结构来维护。

2023.12.21

\(100+80+50+15=245\)

  • C0440 【1221 B组】模拟测试 锁链战车

简单题,直接求每棵树的直径,最后加起来即可。

  • C0440 【1221 B组】模拟测试 刺探军情

题意:给定一个有 \(n\) 个数的序列 \(a\) 以及一个数 \(k\)。最大化区间和与区间 \(\gcd\) 的乘积,且区间长度不小于 \(k\)

首先可以想到用前缀和数组维护区间和,用 ST 表维护区间 \(\gcd\)。假设我们枚举右端点 \(i\),那么就相当于要找到一个左端点 \(j\) 使得原式最大。会发现一个性质:从 \(i\) 往左求 \(\gcd\) 的时候,一共只会有 \(\log n\) 种不同的 \(\gcd\),因为每一次变化至少都会除以二。那么对于同一种 \(\gcd\),我们肯定会贪心地去选最左边的位置,因为这样前缀和最大。所以每一次都二分去找不同的 \(\gcd\) 的最左边的位置并更新答案即可,时间复杂度 \(O(n \log^3 n)\)。大力卡常能过。

再考虑能否优化一个 \(\log\)。我们会发现,每一次二分去找的位置(两个 \(\gcd\) 的断点)其实有很多都是重复的。具体来讲,考虑右端点从 \(i\) 变为 \(i+1\) 的过程,原来不是断点的地方一定还是不是断点,原来是断点的地方不一定是断点。所以每次只需要在上一次的基础上讲中间的一些断点合并一下即可,不用每次都二分去找答案了,时间复杂度 \(O(n\log^2 n)\)

  • C0440 【1221 B组】模拟测试 一击制敌

题意:求一个序列中有多少个区间满足区间众数的出现次数严格大于区间长的一半。

因为一个区间最多只会被一个数产生贡献,那么可先离散化,对每一种数进行计算。对于一个数 \(a\),设 \(cnt_i\) 表示 \(a\)\(i\) 之前出现了多少次。那么将原题限制可以转化为:\(r-2 \times cnt_r<l-2\times cnt_l\),记 \(s=i-2\times cnt_i\)。设 \(lst_i\) 表示上一个 \(a\) 出现的位置,\(nxt_i\) 表示下一个 \(i\) 出现的位置。那么我们每一次怎么修改,其实就是将 \(lst_i\)\(i\) 的所有位置上的 \(s\) 加一(这是一个区间)。如何查询答案,对于每一个位置都是求一个后缀和的形式,而我们最终应该要求所有 \(i\)\(nxt_i\) 的位置的值,所以是一个二阶和。于是问题转化为了区间修改,区间二阶和。

考虑用线段树维护,思考区间加对后缀和的影响。假设区间 \(l\)\(r\) 全部加一,那么 \(l\) 前面的点的后缀和都加的是 \(r-l+1\)\(r\) 后面的点没有变化,而 \(l\)\(r\) 中间的点的后缀和所加的值形成了等差数列(递减)。于是我们只需要区间加,区间加等差数列,区间求一阶和即可。如何区间加等差数列,把 \(l\)\(r\) 的等差数列想象成一个等腰直角三角形。对于左区间,其实是一个梯形,把这个梯形分为上面的等腰直角三角形(递归去做)和下面的正方形(区间加)即可。右边只是一个等腰直角三角形,直接递归去做。时间复杂度 \(O(n \log n)\)

  • C0440 【1221 B组】模拟测试 仓皇逃跑

看到 \(m \le 15\) 且是一道计数题,考虑状压容斥。不好直接统计合法方案数,正难则反。

于是状压出所有情况,对于每一种情况计算不合法的方案数,然后容斥一下即可。

如何计算不合法的方案数?把每一条路径上的边标记一下,那么想要不满足条件,一个联通边的所有边应该为同一个颜色。只需计算有多少联通边即可,可以用并查集维护。

2023.12.23

\(60+80+30+0=170\)

  • C0443 【1223 B组】模拟测试 分数约分

状压出 \(a\) 所有删数的可能,再对应到 \(b\) 去判断一下是否合法即可。注意要开 int128 以及要特判前导 \(0\)

  • C0443 【1223 B组】模拟测试 共享单车

看到数据范围 \(k \le 18\) 很容易想到状压。会发现只有这 \(k\) 个点是有用的,所以先求出这 \(k\) 个点之间的最短距离以及 \(1\)\(n\) 到所有点的最短距离。然后设 \(dp_{S,i}\) 表示已经走过了 \(S\) 这个集合中的点,现在在 \(i\) 点时,到达点 \(n\) 的最短时间。状压转移即可。

2024.1.2

\(100+30+40+20=190\)

  • C0450 【0102 C组】模拟测试 质数统计

用欧拉筛筛出 \(1\)\(10^7\) 的所有质数,将每一个数分解质因数并且将所有因数的答案加一。最后可以用前缀和维护答案,\(O(n)\) 预处理 \(O(1)\) 查询。

  • C0450 【0102 C组】模拟测试 城市规划

题意:给定 \(n\) 个高度 \(h_i\),可以进行 \(k\) 次操作,每次可以改变任意一个高度。最小化 \(\max(|h_i-h_{i+1}|)\)

答案肯定是具有单调性的,考虑先二分答案。问题转化为了对于每一个 \(mid\),判断是否能在 \(k\) 次操作内使得 \(\max(|h_i-h_{i+1}|) \le mid\)。我们设 \(dp_i\) 表示不操作 \(h_i\) 的情况下使得 \(1\)\(i\) 的所有数都合法的最小操作次数(这样设计状态其实是为了方便转移)。那么如果有 \(|a_i-a_j| \le mid \times (i-j)\),则一定可以通过操作 \(j+1\)\(i-1\) 使得 \(i\)\(j\) 全部合法,所以有 \(dp_i=\max(dp_i,dp_j+(i-j-1))\)

最终判断 \(\min(dp_i+(n-i))\) 是否小于等于 \(k\) 即可判断出 \(mid\) 是否合法。

  • C0450 【0102 C组】模拟测试 密码门

题意:有 \(n\) 个操作,每个操作包含一个运算(与,或,异或)以及一个数。
\(q\) 次询问,每次询问有两种情况:

  • 修改一个操作。
  • 给定一个数 \(x\),求出这个数依次进行这 \(n\) 个操作后得到的数。

因为每一种操作都是位运算,所以考虑按每一位来计算。想到线段树,线段树中的每一个区间表示 \(0/1\) 通过这个区间后会变成什么。因为对于每一位都要开一颗线段树,时间复杂度 \(O(n \log^2 V)\)。还可以优化,我们可以只记录 \(0\)\(1023(2^{10}-1)\) 经过一个区间后的值,这样就包含了所以位上的 \(0\)\(1\) 了。那这样两个区间的答案如何合并呢?先考虑 \(0\) 的情况,每一位上的 \(0\) 经过 \([l,r]\) 后的值分为两种情况:

  • \(0\) 先经过 \([l,mid]\) 还是 \(0\)\(0\) 再通过 \([mid+1,r]\)

  • \(0\) 先经过 \([l,mid]\) 变为 \(1\)\(1\) 再通过 \([mid+1,r]\)

\(1023\) 的情况是同理的。

代码(\(f\) 表示 \(0\)\(g\) 表示 \(1023\)):

tree[id].f = (tree[ls].f & tree[rs].g) | ( ~ tree[ls].f & tree[rs].f);
tree[id].g = (tree[ls].g & tree[rs].g) | ( ~ tree[ls].g & tree[rs].f);

最后查询的时候枚举每一位,查询这一位通过整个区间过后的值为多少,加起来即可。

  • C0450 【0102 C组】模拟测试 花环

题意:给定一个 \(n\) 个点的环,每个点上有权值(可能为负数)。你可以从中选出 \(m\) 段,求 \(m\) 段的权值之和的最大值。

首先我们可以把连续的一段负数或者正数合并为一个数。因为如果选了一个正数,那么肯定会选择相邻的正数。如果选了一个负数,那么选这个负数的原因肯定是想把这一段负数旁边的正数连在一起选,所以也必须把这一段负数都选了。把连续的数合并完之后就肯定是一正一负这样的。

如果正数段的数量小于等于 \(m\) 的话,那么直接把全部的正数段选上就行了。否则我们先假定答案为正数段的权值和,然后进行一些操作使得段数等于 \(m\)。有两种操作:

  • 去掉一个正数段:段数减一,答案减少这个正数段的权值和。

  • 用一个负数段连接两个正数段:段数减一,答案加上这个负数段的权值和。

那么我们每一次操作肯定都贪心地去找使答案减少量最少的操作,也就是绝对值最小的段,这个可以用优先队列来维护。另外这个环可以用双向链表维护。

个人认为这个不叫反悔贪心。

2024.1.4

\(100+100+0+20=220\)

  • C0451 【0104 B组】模拟测试 枣树

先求出树的直径 \(len\),如果直径的两个端点都不为根节点的话答案为 \(len+2 \times (m-1) \times \max(depth_u)\)。如果直径的一个端点为根节点的话,还需要求出一条两个端点都是叶子节点的直径 \(len1\),则答案为 \(\max(len1+2 \times (m-1) \times \max(depth_u),len+(m-1) \times \max(depth_u))\)

  • C0451 【0104 B组】模拟测试 对弈

题意:求出 \(n \times m\) 棋盘的 \(k\) 子棋平局局面。

如果 \(k=1\) 或者 \(k=2\)\(\min(n,m)>1\) 则无解。否则每一行两个黑子两个白子这样放即可。

2024.1.16

\(100+0+10+35=145\)

  • C0461 【0116 C组】模拟测试 寻找羔羊

题意:求一个字符串 \(s\) 中有多少个子串包含 \(“agnus”\)

如果上一次出现的位置为 \([i,i+4]\),这一次出现的位置为 \([j,j+4]\)。那么答案就需要加上 \((j-i) \times (n-(j+4)+1)\)。时间复杂度 \(O(n)\),可以扩展到任意长度的匹配串。

  • C0461 【0116 C组】模拟测试 Find and Replace

可以把从每个字符开始的操作过程看成是一棵树,对于每一个字符我们都维护一颗树(表示从这个字符开始操作的过程)。因为后面的操作会覆盖掉前面的,所以我们考虑从最后面的操作开始往上建树。首先 \(a-z\) 都指向自己(因为自己如果不变的话值还是自己),对于每一次操作 \((x,s)\)。我们可以依次合并 \(s_{i-1},s_i\)。合并的过程为:建立一个新点(这个点不代表一个字符,可以设为 '#'),左儿子是 \(s_{i-1}\) 这个字符所表示的树,左儿子是 \(s_{i}\) 这个字符所表示的树。一直合并到 \(s_n\) 为止。最后将字符 \(x\) 所代表的树变为这颗合并完的树。

考虑如何查询答案,我们可以用类似于线段树的方式。对于每一个节点记录一个 \(size\) 表示这个子树内有多少个字符。然后通过 \(size\)\(l,r\) 的大小关系判断应该遍历左区间还是右区间即可。

时间复杂度:因为 \(\sum s \le 200000\),所以建树的时间复杂度是 \(O(n)\) 的。又因为这是一颗二叉树,且最后查询的区间大小(叶子节点个数)是 \(r-l+1=O(n)\) 的,所以查询的时间复杂度也是 \(O(n)\) 的,总时间复杂度 \(O(n)\)

  • C0461 【0116 C组】模拟测试 哞路线

首先有一些性质。因为保证有解,所以 \(a_i\) 一定都是 \(2\) 的倍数(必须一来一回)。并且总的步数应该为 \(\sum a_i\)

先考虑 \(n \le 2\) 的情况,这时我们可以分情况讨论。因为每一条线段都会被来回走两次,所以我们可以先把每一个数都除以 \(2\)

  1. \(a =b\),则最优情况一定是形如 \(RRLLRRLL\dots\) 的,否则转向次数一定更多。

  2. \(a<b\),我们假设先按上述情况的方式走完,那么右边线段应该还需要再被走 \(b-a\) 次。考虑这 \(b-a\) 次在什么时候走最优,会发现如果选择走到中间点的时候再走一次 \(RL\) 是最优的。所以我们每一次走到中间点都可以选择走一次 \(RL\)。最先总共有 \(a\) 个中间点,每走一次 \(RL\) 还会多产生一个中间点,所以总方案数是 \(\frac{\prod_{i=a}^{b-1}i}{(b-a)!}\)

  3. \(a > b\),那么左边线段应该还需要再被走 \(a-b\) 次。则会发现如果走到最左边的时候再多走一次 \(RL\) 是最优的。最先总共有 \((b+1)\) 个左边点,每一次走 \(RL\) 还会多产生一个,所以总方案数是 \(\frac{\prod_{i=(b+1)}^{a}i}{(a-b)!}\)

现在考虑把 \(n\) 扩展到 \(10^5\)。这时我们会发现,如果我们从左往右考虑的话,那么 \(a_{i+1}\) 的选择只和 \(a_i\) 有关系,和 \(a_{i-1}\) 没有关系。所以我们可以从左往右依次将序列拆分为 \([a_1,a_2],[a_2,a_3]\) 这样长度为 \(2\) 的段,对于每一段都可以按照 \(n=2\) 的情况单独算,最终答案即为它们相乘。

  • C0461 【0116 C组】模拟测试 关灯

一次操作相当于把 \(a\) 异或上 \(b\),修改开关的一位相当于将这一位异或上 \(1\)

会发现一个很神奇的性质:初始开关对灯的影响和改变开关状态对灯的影响是独立的。而前者的影响是固定的,所以我们可以只考虑改变开关状态对灯的影响。假设一共需要 \(k\) 次操作能使所有灯关闭,如果我们在第 \(i\) 次操作的时候改变了开关 \(p\) 的状态,那么第 \(i+1\) 次的时候这个开关会影响到 \(p+1\)(因为要循环右移一位),第 \(i+2\) 次操作会影响到 \(p+2\)。所以如果在第 \(i\) 次操作改变一个开关的状态,那么相当于将长度为 \(m-i+1\) 的一段区间全部影响了(异或上 \(1\))。那么如果我们一共需要 \(k\) 次操作,那么我们可以分别修改长度为 \(1\)\(k\) 的区间各一次。注意这里的区间指的不一定是连续的,也有可能是一段前缀加一段后缀(因为可能循环右移)。并且如果一个点被修改(异或)两次的话相当于不修改。

\(dp_{i,j}\) 表示 \(i\) 次操作(第 \(i\) 次操作指的是将一段长度为 \(i\) 的区间异或上 \(1\))之后能否达到 \(j\) 状态。那么转移方程为 \(dp_{i,j}=dp_{i,j} | dp_{i-1,j ^ k}\),其中 \(k\) 是长度为 \(i\) 的区间。时间复杂度 \(O(2^n\times n^2)\),可以通过。

还可以优化。如果一个状态是另一个状态通过循环右移得到的,那么这两个状态的 \(dp\) 值一定相同,可以只用记录一次。时间复杂度 \(O(2^n \times n)\)

2024.1.18

\(100+28+20+64=212\)

  • C0463 【0118 C组】模拟测试 序列

题意:给定 \(n\) 个数以及两个整数 \(k,m\)。对于每一个 \(1 \le i \le m\) 求出在原数组中选出 \(k\) 个数使它们的 \(\gcd\)\(i\) 的方案数。

首先预处理出 \(s_i\) 表示 \(i\) 的倍数的数有多少个。设 \(f_i\) 表示 \(i\) 的答案,初始为 \(s_{i}^{k}\)。但是我们会发现如果这 \(k\) 的数的 \(\gcd\)\(i\) 的倍数的话是不应该计算在内的,所以要将 \(f_i\) 减去 \(\sum_j f_{i \times j}\)

  • C0463 【0118 C组】模拟测试 金条争夺

首先容易发现抢金条的操作只会出现一次,否则第一次抢金条的那个人一定不会去抢。并且如果一个人要抢金条,当且仅当没人能从他手里再抢过来。

会发现这和二分图博弈的模型是完全等价的,可以用二分图博弈判断出会不会发生抢金条的操作。

如果先手必败的话,那么一定不会出现抢金条的情况,最终状态和初始状态相同。

如果先手必胜,假设金条在 \(x\)。那么我们可以从 \(1\) 开始枚举 \(i\),如果 \((i \to x)\) 这条边是二分图最大匹配的可行边的话,那么 \(i\) 就会抢过金条。

二分图博弈 & 二分图最大匹配的可行边

2024.2.3

\(100+99+61+42=302\)

  • C0478 【0203 C组】模拟测试 独立集

题意:给定一张 \(n\) 个点 \(m\) 条边的无向图。求出这个图的所有子图的最大独立集的和。

数据范围:\(n \le 26,m \le \frac{n \times (n-1)}{2}\)

看到这个数据范围,考虑状压 dp。设 \(dp_i\) 表示包含了 \(i\) 中这些点的子图的最大独立集大小。那么我们可以从 \(lowbit(i)\) 进行转移。如果不选它的话,那么 \(dp_i=dp_{i-lowbit(i)}\)。如果要选的话,那么 \(dp_i=dp_{i \land(\neg e[lowbit(i)])}+1\),其中 \(e_u\) 表示所有和 \(u\) 相邻的点的集合。转移时讲这两种情况取 \(\max\) 即可。时间复杂度 \(O(2^nn)\)。最终的答案就是所有的 dp 值相加。

2024.2.20

\(100+100+70+50=320\)

  • C0487 【0220 C组】模拟测试 音乐变奏

题意:给定一个长度为 \(n\) 的序列,你可以给一个长度为 \(m\) 的区间加上一个首项为 \(c\),公差为 \(d\) 的等差数列。最大化这个序列的第 \(k\) 大值。

数据范围:\(n \le 2\times 10^5\)

因为题目要求最大化第 \(k\) 大,我们可以用二分。先二分出一个 \(mid\) 表示第 \(k\) 大是多少。假设序列中已经大于等于 \(mid\) 的数有 \(sum\) 个。对于每一个比 \(mid\) 小的数,我们思考将这个等差数列的首项设在哪里才能使得这个数最后变为大于 \(mid\)。会发现合法的位置一定是一段区间,我们可以将这一段位置全部加 \(1\),表示在这些位置放等差数列的首项可以使得一个数从小于 \(mid\) 变为大于 \(mid\)。最后只需要看一下是否存在一个位置的值大于等于 \(k-sum\) 即可。可以用线段树维护,也可以直接差分,时间复杂度 \(O(n \log V \log n)\)\(O(n \log V)\)

这道题启发我们如果不好直接求出对于每一个 \(A\) 能使哪些 \(B\) 满足条件可以尝试去求出对于每一个 \(B\) 有哪些 \(A\) 能使 \(B\) 满足条件。

  • C0487 【0220 C组】模拟测试 数学作业

题意:有 \(n\) 个人,每一个人可以选择去城市 \(a_i\) 或者城市 \(b_i\)。最小化有奇数个人去的城市的个数。

数据范围:\(n\le 10^5\)

首先我们可以将 \(a_i\)\(b_i\) 连一条无向边。设 \(s_i=0/1\) 表示去 \(i\) 城市的人的奇偶。考虑一个人如果从选 \(a_i\) 变为选 \(b_i\) 会对这两个城市的奇偶性有什么影响,会发现一共只会有四种变化:

  • (一):\((0,0) \to (1,1)\)
  • (二):\((1,1) \to (0,0)\)
  • (三):\((0,1) \to (1,0)\)
  • (四):\((1,0) \to (0,1)\)

我们先假设所有人都去 \(a_i\)。那么对于一个连通块,假设这个联通块中一共有 \(x\)\(1\)。如果 \(x\) 为偶数,那么一定有一种改变选择的方式使得这个联通块都为 \(0\),具体操作是先通过(三)(四)把每两个 \(1\) 放在一起,再通过(二)将它们全部变为 \(0\)。但是如果 \(x\) 为奇数的话就必须要剩一个 \(1\),因为每一次只能两个两个地消。

所以最终的答案就是 \(\sum( size\mod 2)\),其中 \(size\) 是连通块中 \(s_j=1\) 的数量(假设每个人都选 \(a_i\))。

2024.2.28

\(40+30+5+32=107\)

  • C0492 【0228 B组】模拟测试 网格

题意:给定一个包含数字或加号或乘号的矩阵,求所有从 \((1,1)\)\((n,m)\) 的路径组成的表达式的值的和。

一道和本题很像的题(ABC 338 G)

考虑从 \((n,m)\) 开始,从后往前进行 \(\text{dp}\),从后往前比较好做。

\(dp_{i,j}\) 表示从 \((i,j)\)\((n,m)\) 的答案,显然不能直接做,因为无法直接判断当前 \(a_{i,j}\) 对前面所有位置的贡献。所以可以考虑记录一些其它的值来辅助转移。设 \(num_{i,j}\) 表示从 \((i,j)\) 开始,所有连乘(直到加号为止)的和,\(cnt_{i,j}\) 表示 \((i,j)\)\((n,m)\) 中有多少个数字,\(p_{i,j}\) 表示 \((i,j)\) 位置的数对后面的贡献需要乘上多少。

可以分情况进行转移。

dp[i][j] = (dp[i][j] + dp[i][j + 1]) % mod;
cnt[i][j] = (cnt[i][j] + cnt[i][j + 1]) % mod;
if(a[i][j] == '+' || a[i][j] == '*') 
	num[i][j] = (num[i][j] + num[i][j + 1]) % mod;
else if(a[i][j + 1] == '+')
	dp[i][j] = (dp[i][j] + (a[i][j] - '0') * cnt[i][j + 1]) % mod,
	num[i][j] = (num[i][j] + (a[i][j] - '0') * cnt[i][j + 1]) % mod,
	p[i][j] = (p[i][j] + 10 * cnt[i][j + 1]) % mod; 
else if(a[i][j + 1] == '*')
	dp[i][j] = (dp[i][j] + (a[i][j] - '0' - 1) * num[i][j + 1]) % mod,
	num[i][j] = (num[i][j] + num[i][j + 1] * (a[i][j] - '0')) % mod,
	p[i][j] = (p[i][j] + num[i][j + 1] * 10) % mod;
else
	dp[i][j] = (dp[i][j] + (a[i][j] - '0') * p[i][j + 1]) % mod,
	num[i][j] = (num[i][j] + num[i][j + 1] + (a[i][j] - '0') * p[i][j + 1]) % mod,
	p[i][j] = (p[i][j] + p[i][j + 1] * 10) % mod;

2024.3.2

\(100+70+30+60=260\)

  • C0493 【0302 B组】模拟测试 棋盘

题意:有一个 \(n \times m\) 大小的棋盘,每一个位置有 \(0\)\(1\) 两种颜色。每一次操作可以选择一个极大的同色联通块,并反转这个联通块中所有的颜色。给定最终的情景,求有多少种合法的初始情况。

首先我们考虑最终状态中两个不同色极大联通块交界的点(形如 \(01\)\(10\)),这些点的初始颜色可以是什么。首先不可能同色,否则最终它们一定也同色。那能否是最终颜色的相反值呢?其实也不行,因为如果是这样的话那必须先反转其中一个,但是反转之后这两个位置就是同色的了,并且不会再变为不同色,所以不行。

综上所述,对于两个联通块的交界部分,最初状态一定和最终状态相同。那对于联通块内的点呢,会发现无论最初是什么情况,最终都一定可以将它们全部变为一个颜色。所以如果两个联通块的交界部分的点数为 \(sum\),则答案为 \(2^{n \times m - sum}\)

  • C0493 【0302 B组】模拟测试 序列

题意:给定一个长度为 \(n(n \le 4000)\) 的序列 \(w\)。对于一个满足 \(a_i \le n\) 的序列 \(a\),定义它的贡献为:

\[\sum_{1 \le i \le i-2}w_{\max(a_{i},a_{i+1},a_{i+2})} \]

求出所有序列 \(a\) 的贡献总和。

首先考虑暴力 dp,设 \(dp_{i,j,k}\) 表示当前长度为 \(i\),最后一个数为 \(k\),倒数第二个数为 \(j\) 的贡献总和,那么就可以从 \(dp_{i-1,p,j}\) 转移过来,时间复杂度 \(O(n^4)\)

考虑优化,会发现其实贡献只和后三个数之中的最大值有关,所以可以不用枚举全部位置的值,而是枚举后三个数中的最大值以及最大值的位置。所以设 \(dp_{i,j,k \in \{0/1/2\}}\) 表示序列长度为 \(i+2\),最大值为 \(j\),且最大值的位置在 \(i+k\) 的答案(且不考虑 \(i+k\) 之后的取值),这样复杂度就优化到了 \(O(n^3)\)。但是会发现这个 dp 转移的式子全都是之前的 dp 值的一段区间,可以用前缀和优化,时间复杂度 \(O(n^2)\)

2024.3.5

\(100+100+20+20=240\)

  • C0494 【0305 B组】模拟测试 树

题意:给定一颗 \(n\) 个点的树,每个点可能为黑色,白色或者无色,边有边权。称一个结果合法当且仅当没有连通块同时包含黑色和白色的点,求最少删去边权和为多少的边使得结果合法。

\(dp_{i,j \in \{0,1,2\}}\) 表示将 \(i\) 这颗子树变合法,且 \(i\) 这个点的连通块颜色为无色 / 黑色 / 白色的最小代价。对于每一个点从它的所有儿子转移,时间复杂度 \(O(n)\)

  • C0494 【0305 B组】模拟测试 背包

题意:有 \(n\) 种物品,每种物品有无穷个并且有一个大小和价值。有 \(m\) 个人,每个人有一个容量为 \(c\) 的背包,每个人会从大小最大的物品开始拿(每个物品拿一个,如果大小相同则拿价值大的),求每个人最终得到的物品的价值是多少。

首先将所有物品以大小为第一关键字,价值为第二关键字降序排序,会发现每个人取的物品一定是一段一段的,并且段数一定是小于等于 \(\log c\) 的。因为每选一段容量一定除以二。证明:假设当前容量为 \(x\),如果选了一段之后容量不小于 \(\frac{x}{2}\),那一定可以再选一个数。所以每一次先通过二分找到一段的起点,再二分找到这一段的终点即可,时间复杂度 \(O(m \log n \log c)\)

  • C0494 【0305 B组】模拟测试 跳跃

题意:有 \(n\) 个点的编号为 \(1\)\(n\),第 \(i\) 个点可以走到编号为 \([L_i,R_i]\) 的点上。求距离最远的两个点的距离。

首先会发现一个性质,每一个点走 \(k\) 步能够到达的点一定是一个区间。那可以设 \(l_{i,k}\) 表示从 \(i\) 开始走 \(k\) 步能到达的最左边的点是多少,\(r_{i,k}\) 同理。那么有 \(l_{i,k}=\min(l_{j,\frac{k}{2}})(l_{i,\frac{k}{2}} \le j \le r_{i,\frac{k}{2}})\)。其中 \(\min\) 的值可以用 st 表维护。但是会发现 \(l\) 的状态 \(O(n^2)\) 的。于是我们可以也用一个 st 表来维护 \(l\) 的值,也就是 \(l_{i,k}\) 表示从 \(i\) 开始走 \(2^k\) 步能到达的最左边的点是多少。

然后我们可以二分答案,对于每一个 \(mid\),如果存在两个点 \(i,j\) 满足从 \(i\)\(mid\) 步走不到 \(j\),从 \(j\)\(i\) 步也走不到 \(i\),那么答案一定大于 \(mid\)。否则一定小于等于 \(mid\)

但是我们不好直接求距离 \(i\)\(mid\) 的点,所以我们可以把二分换成枚举每一个二进制位然后贪心能加就加,不能加就不加。这样就可以用之前预处理出的 \(l\)\(r\) 数组进行距离计算了。

2024.3.9

\(100+30+40+30=200\)

  • C0495 【0309 B组】模拟测试 最小值

题意:有 \(n(n \le 10^7)\) 个数,初始值为 \(+\infty\)。有 \(m(m \le 10^7)\) 次询问,每一次给定 \(a\)\(b\),将第 \(a\) 个数修改为 \(b\),并求出 \(n\) 个数中的最小值。数据保证 \(a\)\(b\) 随机生成

正常的想法应该是用 ds 去维护全局最小值,但是 \(n,n\) 太大会导致 TLE。考虑到数据保证 \(a\)\(b\) 随机生成,所以我们可以动态维护一个全局最小值 \(\min\),如果当前要修改的 \(a\) 位置上的数等于 \(\min\) 的话就 \(O(n)\) 暴力去算当前新的 \(\min\),否则 \(\min\) 不变。因为 \(a\) 位置上的数等于 \(\min\) 的概率为 \(\frac{1}{n}\),所以每 \(n\) 次会执行一次暴力,总复杂度 \(O(n)\)

  • C0495 【0309 B组】模拟测试 字符串

题意:给定两个字符串 \(s\)\(t\)。你可以对 \(s\) 进行四种操作(代价分别为 \(a,b,c,d\)):任意位置增加任意一个字符,任意位置删除一个字符,替换一个字符,交换相邻两个字符。求将 \(s\) 变为 \(t\) 的最小代价,数据保证 \(a+b \le 2 \times d\)

考虑 dp。设 \(dp_{i,j}\) 表示将 \(s\) 的前 \(i\) 个字符变为 \(t\) 的前 \(j\) 个字符的最小代价,可以分操作转移。
对于操作一:\(dp_{i,j}=dp_{i,j-1}+a\)
对于操作二:\(dp_{i,j}=dp_{i-1,j}+b\)
对于操作三:\(dp_{i,j}=dp_{i-1,j-1}+c\)
对于操作四,因为数据保证 \(a+b \le 2 \times d\),所以一个数一定不会被交换两次及以上,并且交换后不会被替换。

前者很好证明,考虑证明后者。假设交换会被替换,则有:

\[\begin{cases}c+d < c+c \\c+d < a+b \\a+b < 2 \times d \end{cases} \]

解得 \(d > c\)\(d < c\),无解,所以交换后一定不会再被替换。

于是我们记 \(k\)\(s\) 中上一个 \(t_j\) 的位置,\(l\)\(t\) 中上一个 \(s_i\) 的位置,dp 转移为:
\(dp_{i,j}=dp_{k-1,l-1}+d+b \times (i-k-1)+a \times (j-l-1)\)

总时间复杂度 \(O(nm)\)

  • C0495 【0309 B组】模拟测试 跳码

题意:求马从 \((0,0)\) 走到 \((x,y)\) 的最小步数。

分类讨论题。做一大堆分讨,主要思路是先将马走到距离 \((x,y)\) 不超过 \(3\) 的位置,然后暴力计算。

2024.3.27

省选模拟赛,\(65+30+0=95\)

  • C0503 【0327 A组】模拟测试 小 G 的布料

题意:求一个 \(n\times m\)\(01\) 矩阵中面积大于等于 \(k\) 且全部都为 \(0\) 的子矩阵的个数。(\(n \le 2000\)

首先我们考虑几个子问题。

子问题一:\(n \le 500\)

先预处理出 \(nxt_{j,i}\) 表示第 \(j\) 列中第 \(i\) 个数后面第一次出现 \(1\) 的位置。

首先我们枚举子矩阵的左上角 \((i,j)\),然后再枚举子矩阵的右边界 \(r\),那么当前右边界上可以作为子矩阵右下角的点的数量为 \(\max(0,\min_{p=i}^{r}(nxt_{p,i}-i)-\frac{k-1}{r-i+1})\)。记录前缀 \(\min\) 即可,时间复杂度 \(O(n^3)\)

子问题二:求最大的且全部都为 \(0\) 的子矩阵。

先枚举上边界 \(i\),会发现每一列的连续的 \(0\) 组成了一个直方图的形式,那么我们可以用单调栈求出每一列 \(j\) 往左右最多可以扩展到的 \(l,r\)。那么 \(ans=\max(ans,(r-l+1)\times(nxt_{j,i}-i))\)。时间复杂度 \(O(n^2)\)

回到本题。先枚举上边界 \(i\),对于每一列 \(j\),我们还是可以用单调栈求出左右端点 \(l,r\)。那么其实要求的就是在长为 \(r-l+1\),宽为 \(nxt_{j,i}-i\) 的矩形中,端点在上边界且面积大于等于 \(k\) 的子矩形数量。这个可以用 \(O(n^2)\) dp 预处理出。但是会发现有一部分会算重,这一部分其实就是长为 \(r-l+1\),宽为 \(\max(nxt_{l-1,i},nxt_{r+1,i})-i\) 的矩形(把直方图画出来会更容易理解),把这一段减去即可。时间复杂度 \(O(n^2)\)

2024.4.16

\(100+10+0+55=165\)

  • C0511 【0416 B组】模拟测试 U

首先会发现我们只需要求出每一个石砖到终点的最短路。因为对于每一个冰砖,可以先走到四个方向上最近的石砖上,再通过这个石砖来计算答案。

石砖有很多个,但是终点只有一个,这相当于是多源一汇的。我们可以把它转化为从终点走到所有石砖,这样就是普通的单源最短路了。因为速度会变,于是我们可以用分层图的思想,将不同的速度分层,当前层与下一层之间连边,边权为层数(速度)。那我们要求的就是点数 \(O(n^2)\),边数 \(O(n^2)\) 的单源最短路,但是带 \(\log\) 过不了。但是会发现这是个 DAG,因为只在层与层之间连边,所以不会出现环,于是直接 dp 即可。因为我们是从终点反过来走到起点,所以层(速度)也要反过来(从大到小)考虑。转移方程为:\(dp_{u,i}=\min(dp_{v,i+1}+i\times w(u,v))\)。时间复杂度 \(O(n^2)\)\(n\) 是石砖的个数。

  • C0511 【0416 B组】模拟测试 T

很妙的题。首先将 \(a\) 降序排序。然后分类讨论,讨论除了 \((x,y,z)\) 之外的最大值的位置在哪(最多 \(4\) 种情况),会发现每种情况中都有一个充分必要条件,于是很好计算答案。

  • C0511 【0416 B组】模拟测试 S

题意:给定 \(n\) 个数 \(a_i\),你可以将其中的 \(m\) 个变为 \(0\)。另外给定 \(q\) 个区间 \([l_i,r_i]\),最小化 \(\sum_{i=1}^{q} (\min_{l_i\le j \le r_i}a_j)\) 的值。\((n,q \le 50)\)

首先我们把一个区间的贡献映射到这个区间中没有变为 \(0\) 中最大的数上。那么假设我们按 \(a_i\) 从大到小考虑,如果将 \(a_i\) 变为 \(0\) 的话,那么不需要计算 \(a_i\) 的贡献;如果不变的话,那么对于包含 \(a_i\) 且没有被删的区间,都产生 \(a_i\) 的贡献(因为 \(a_i\) 是从大到小枚举的),然后并且将这些区间删去,继续考虑不包含 \(a_i\) 的区间。

发现这个过程有点像递归,又有点像区间 dp。我们设 \(dp_{k,m,l,r}\) 表示当前从大到小考虑已经考虑完 \(a_k\) 了,还能最多将 \(m\) 个数变为 \(0\),且只考虑在 \([l,r]\) 中的区间的最小贡献。

考虑如何转移,首先因为已经考虑完 \(a_k\) 了,所以我们找到 \([l,r]\) 中小于等于 \(a_k\) 且还没有考虑到的最大值 \(a_{id}\)。如果将 \(a_{id}\) 变为 \(0\),那么 \(dp_{k,m,l,r}=dp_{id,m-1,l,r}\)。如果不变,那么首先要计算出包含 \(id\) 的区间的个数 \(sum\),则产生的代价为 \(sum \times a_{id}\),且之后不用考虑包含 \(id\) 的区间了,所以 \(dp_{k,m,l,r}=(\min_{t=0}^{m}dp_{id,t,l,id-1}+dp_{id,m-t,id+1,r})+sum \times a_{id}\)

会发现这样的 dp 方程是递归形式的,不好直接朴素转移。所以我们可以采用记忆化搜索的的方式来转移,状态数 \(O(n^4)\),每一次转移 \(O(n)\),总时间复杂度 \(O(n^5)\)

2024.4.30

\(80 + 45 + 60=185\)

  • C0516 【0430 B组】模拟测试 ⼩⻩鸭与矩阵

题意:给定 \(n,k\)。你要构造一个 \(n \times n\)\(01\) 矩阵 \(A\),满足 \(A^k\) 为全 \(0\) 矩阵。求 \(A\) 矩阵中最多有多少个 \(1\)

因为 \(A\)\(01\) 矩阵,所以可以把它看成一个 \(n\) 个点的图的邻接矩阵。关于邻接矩阵的 \(k\) 次方有一个很经典的 Trick:若 \((A^k)_{i,j}=1\),则 \(i\)\(j\) 一定有一条长度为 \(k\) 的路径,反之则没有。那么题目的要求就变为了 \(n\) 个点的图中没有长度大于等于 \(k\) 的路径(显然也没有环)。

最终 \(1\) 的个数最多相当于图的边数最多。可以将 \(n\) 个点平均分成 \(k\) 块,每一块之间的点相互都不连边。每一块与它前面的所有块中的点都连边,这样一定没有长度 \(\ge k\) 的路径,且一定最优。

  • C0516 【0430 B组】模拟测试 ⼩⻩鸭与涂⾊画

题意:定义一个 \(01\) 网格图 \(A\) 的代价 \(f(A)\) 如下:

  • 若网格全 \(0\) 或全 \(1\),则代价为 \(0\)
  • 否则将网格 \(A\) 水平或竖直切一刀分成 \(B\)\(C\),代价为 \(\min_{B,C}(\max(f(B),f(C)) + 1)\)
    求一个 \(n \times m(n,m \le 250)\) 的网格的代价。

弱化版:AGC033D

首先我们容易想到一个暴力的二维区间 dp,设 \(dp_{i,j,p,q}\) 表示 \((i,j)\)\((p,q)\) 的答案,枚举转移点 \(O(n)\),时间复杂度 \(O(n^5)\)
考虑挖掘一些性质。会发现如果每一次切成一半的话那么答案是 \(\log n + \log m\) 级别的,这个值很小。

所以我们可以交换 dp 的答案和状态,也就是设 \(dp_{i,j,p,k}=q\) 表示满足「\((i,j)\)\((p,q)\) 的答案为 \(k\)」的最大的 \(q\)。这样对于横着切的情况可以 \(O(1)\) 转移了,但是竖着切的情况还是需要 \(O(n)\) 转移,时间复杂度 \(O(n^4 \log n)\)

继续观察,会发现对于同一组 \((i,j,k)\),若 \(p\) 单调递增,则每次竖着切的转移点也一定都是单调不降的。所以可以用单指针维护竖着切的转移点的位置,均摊下来单次转移时间复杂度 \(O(1)\),总时间复杂度 \(O(n^3 \log n)\),可以通过。

2024.5.9

\(100+80+50+30=260\)

  • C0518 【0509 B组】模拟测试 购票方案

\(dp_{i}\) 表示前 \(i\) 次的答案。若当前买单次票,则 \(dp_i=dp_{i-1}+One\),若买时限票,则 \(dp_i=\min_{k} (dp_{j-1}+cost_k)\),其中 \(j\) 为该时限票的时间最多能覆盖到哪一次。那么每一次都需要用 \(\log n\)(二分)的时间去求 \(j\)。总时间复杂度 \(O(nk \log n)\)

再观察一下就会发现对于每一个 \(k\),若 \(i\) 单调递增,则 \(j\) 也一定单调不降。于是可以用单指针维护,每次转移均摊 \(O(1)\)。总时间复杂度 \(O(nk)\)

  • C0518 【0509 B组】模拟测试 直线相交

题意:有 \(n\) 条直线,问是否能恰好有 \(m\) 个交点。\((n \le 500,m \le 10^5)\)

首先可以设 \(dp_{i,j}\) 表示 \(i\) 条直线是否能构成 \(j\) 个交点,枚举平行的直线数量转移,复杂度 \(O(n^2m)\)

会发现上述的 dp 答案是 bool 类型的,给人一种很亏的感觉。考虑换一种 dp 状态,假设我们把「\(n\) 条直线有 \(\frac{n(n-1)}{2}\) 个交点」称为全满状态。设 \(dp_i\) 表示若想要比全满状态少恰好 \(i\) 个点,一共最少需要几个点。枚举平行的直线数量转移,时间复杂度 \(O(nm)\)

  • C0518 【0509 B组】模拟测试 模式匹配

题意:给定一个 \(n\) 个数的序列 \(a\)。一个序列 \(b\) 合法当且仅当:

  • \(b\)\(a\) 的子序列
  • \(b=F+F+\dots+F\),其中 \(F\) 指的是形如 \((a,b,a,b)\) 的四元组。
    要求最大化 \(b\) 的长度。

首先我们设 \(dp_i\) 表示以 \(a\) 的第 \(i\) 项为结尾的答案,那么 \(dp_{i}=dp_j+4\),其中 \(j\) 表示的满足 \([j+1,i]\) 中的数能构成 \((a,b,a,b)\) 的最后一个位置。那么考虑如何求 \(j\),其实本质就是对于 \(i\) 求一组 \((a,b,a,b)\) 使得第一个 \(a\) 的下标最小。有一个非常巧妙的结论:假设我们固定了两个 \(b\) 的位置,那么两个 \(a\) 的中间一定没有 \(a\)。同理固定了两个 \(a\) 的位置,那么两个的 \(b\) 中间也一定没有 \(b\)。证明可以用调整法。所以相当于两个 \(a\) 和两个 \(b\) 一定是“相邻”的。

那么这就可以用线段树维护了,首先求出第 \(i\) 个数下一次出现的位置 \(nxt_i\)。那么假设第一个 \(b\) 出现在 \(i\),则第二个 \(b\) 一定出现在 \(nxt_i\)。考虑如何让第一个 \(a\) 的下标最小。我们可以建立一颗线段树,每一个 \(nxt_{j}\) 位置的值为 \(j\)。那么每一次查询其实就是区间 \([i+1,nxt_i-1]\) 的最大值 \(mx\)。转移方程为 \(dp_{nxt_i}=dp_{mx}+4\)。时间复杂度 \(O(n \log n)\),注意如果 \(a=b\) 的情况需要特殊处理一下。

2024.5.15

\(80+20+100=200\)

  • C0520 【0515 C组】模拟测试 医院修建

题意:有 \(n\) 个点,你可以花 \(c_i\) 的代价在 \(i\) 号点上修一个医院,也可以花 \(w_i\) 的代价连接 \((u_i,v_i)\) 这条无向边。求所以点都能走到医院的最小花费。

首先我们考虑只有一个医院的情况,会发现这就是一个最小生成树问题。那如果有多个医院的话,其实就是“最小生成森林”,当然森林中的每棵树都需要在根节点产生一个 \(c_i\) 的代价。

直接做是麻烦的。我们可以把它转化为最小生成树问题。有一个小 Trick:考虑建立一个虚拟点,它向每一个点 \(i\) 连一条边权为 \(c_i\) 的边。神奇的是原题的答案正好等于这颗 \(n+1\) 个点的无向图的最小生成树,于是就可以直接求解了。

  • C0520 【0515 C组】模拟测试 厂州塔

题意:有一个最低为 \(1\) 层,最高为无限层的塔。你现在在第 \(k\) 层。你需要执行(不一定按顺序)\(n\) 个操作,每一个操作包括两个数 \((a,b)\) 表示你要先下 \(a\) 层然后再上 \(b\) 层,问你能否顺利执行完这 \(n\) 操作。

首先我们考虑所有 \(a_i<b_i\) 的操作。我们肯定先执行这种操作,因为每次操作完层数一定会增加。我们可以按 \(a_i\) 从小到大排序,依次执行操作。为啥要按 \(a_i\) 从小到大排序?因为 \(a_i\) 越大肯定越不容易合法,所以就要放到后面,让前面先多加一点。

接着考虑 \(a_i > b_i\) 的操作,直接贪心不容易发现策略,我们可以把它转化为上面的情况。具体的,我们设 \(K=k-\sum(a_i-b_i)[a_i>b_i],A_i=b_i,B_i=a_i\)。这就变得和上述情况相同了。其实我们就是把整个过程倒过来了。

两种情况都可以在 \(O(n)\) 的时间复杂度内完成。

posted @ 2023-12-08 08:32  Creeper_l  阅读(23)  评论(1编辑  收藏  举报