2022 省选做题记录 2.0

目录

2.25

「JOISC 2015 Day 3」Card Game Is Great Fun(DP 优化状态)

容易发现当前牌堆一定会形如 i, j, k, k + 1, ..., n

不难想到一个 \(\mathcal{O}(n^4)\) 的 DP:设 \(f_{p,i,j,k}\) 表示上一张牌选 \(p\),当前牌堆第一张是 \(i\),第二张是 \(j\),第三张是 \(k\) 的最大价值。

转移后会有两种情况:

  • \(p=i\),则此时牌堆变成 j, k, k + 1, k + 2, ..., n,即 \(f_{p,i,j,k}+v_i\to f_{i,j,k,k+1}\)
  • \(p=k\),则此时牌堆变成 i, j, k + 1, k + 2, ..., n,即 \(f_{p,i,j,k}+v_k\to f_{k,i,j,k+1}\)

容易发现要么 \(k=j+1\),要么 \(k=p+1\)

那么我们可以改变状态:设 \(f_{p,i,j,0/1}\) 表示上一张牌选 \(p\),当前牌堆第一张是 \(i\),第二张是 \(j\),第三张是 \(j+1/p+1\) 的最大价值。

直接转移即可。

Code:https://loj.ac/s/1390444

「CF1310C」Au Pont Rouge(*2800,求 LCP + 二分 + DP)

问题满足可二分性,于是考虑怎么二分字符串。

首先可以通过求出 LCP 比较两个字符串的大小。于是我们把所有子串全都拿出来排序,在新的序列里二分。

一个显然的想法是设 \(dp_{i,j}\) 表示 \([1,i]\) 分成 \(j\) 段且每一段都大于等于当前二分的字符串的方案数。复杂度 \(\mathcal{O}(n^3)\),且很难再优化。

那么可以考虑到在字符串的后面加字符是不会降低字典序的,于是就可以设成 \([i,n]\) 分成 \(j\) 段且满足条件的方案数。

容易发现有贡献的一定是一段后缀,提前计算好这个后缀,然后后缀和优化 DP 即可。

Code:https://codeforces.live/problemset/submission/1310/147621176

「BZOJ4403」序列统计(组合数学)

题意:

给定三个正整数 \(n\)\(l\)\(r\),统计长度在 \(1\)\(n\) 之间,元素大小都在 \(l\)\(r\) 之间的单调不降序列的数量。输出答案对 \(10^6+3\) 取模的结果。

首先可以将值域转成 \([1,r-l+1]\)。设 \(len=r-l+1\)

然后写出式子:

\[\begin{aligned} &\sum\limits_{i=1}^n \binom{len+i-1}{i}\\ =&\sum\limits_{i=1}^n \binom{len+i-1}{len-1}\\ =&\sum\limits_{i=0}^n \binom{len+i-1}{len-1}-1\\ =&\binom{len+n}{len}-1 \end{aligned} \]

Lucas 定理计算即可。

Code:https://paste.ubuntu.com/p/yJv4HBw7zZ/

「CF704B」Ant Man(*2500,排列计数 DP)

与「ZJOI2012」波浪一样的套路,记录连通块个数。

\(f_{i,j}\) 表示已经填了 \(i\) 个数,有 \(j\) 个连通块的最小权值。

转移分 新开一段、合并两段、向左 / 向右延伸 讨论。

注意不合法的情况要去掉,\(s\)\(e\) 单独讨论一下。

写代码的时候推荐写从 \(f_{i-1,j}\) 转移到 \(\{f_i\}\),细节相对较少。

Code:https://codeforces.live/problemset/submission/704/147641413

「ABC239F」Construct Highway(构造)

题面

题意:

给出 \(m\) 条边和一个数组 \(\{d_i\}_{i=1}^n\),问能否构造出一棵树使得这棵树的边集包含这 \(m\) 条边且每个点 \(i\) 的度数为 \(d_i\)\(n\le2\times 10^5\)

首先可以把读入之后不满足的情况判掉。也就是加的边形成环或者一个点度数已经超过了限制。

把每个点加入度数个到它并查集集合的一个 vector 里面。

然后问题转化成:能不能构造一棵树满足度数条件。

有两种做法:

  • 将并查集集合分成 vector 大小 \(=1\)\(\ge2\) 的,每次将一个 \(\ge2\) 的集合与 \(=1\) 的集合匹配,并且最后留一个元素放到 \(=1\) 的集合里面。最后会剩下两点一边。

  • 将集合按照度数从大到小加入到一个优先队列里,每次取出度数最大的两个集合合并。

官方题解的做法 1:https://atcoder.jp/contests/abc239/submissions/29229844

做法 2:https://atcoder.jp/contests/abc239/submissions/29639757

2.26

2.26 考试 T2 序列(贪心)

题意:

给一个长度为 \(n\) 的序列 \(a_{1\dots n}\),你可以无限次进行以下操作:

  • 选择一个下标 \(i\in[2,n-1]\),依次执行:
    • \(a_{i-1}\leftarrow a_{i-1}+a_i\)
    • \(a_{i+1}\leftarrow a_{i+1}+a_i\)
    • \(a_i\leftarrow -a_i\)

你需要最小化 \(\max\limits_{i=1}^n|a_i|\)

数据范围:\(1\le T\le 3\)\(3\le n\le3\times10^5\)\(|a_i|\le10^9\)

首先可以看出来这个操作等价于交换 \(1\sim n-1\) 的前缀和数组中的相邻两项。

然后就相当于固定 \(sum_0=0\)\(sum_n\),重排 \(sum_{1\dots n-1}\),使得 \(\max|sum_i-sum_{i-1}|\) 最小。

那就把 \(0\)\(sum_{1\dots n}\) 一起排个序,设起点为 \(0\),终点为 \(sum_n\),找出一条哈密顿路使得上面的式子最小。

不妨设 \(sum_n\ge 0\),那么把 \(0\)\(sum_n\) 拿出来,隔一个走、中间走相邻的是最优的。

如图:

屏幕截图 2022-02-26 223844.png

证明可以邻项交换。

2.26 考试 T3 重开(二分 + DP)

题面:

有一个有 \(n\) 个点和 \(m\) 条边的【有向无环图】。每条有向边形如 \((u,v,w,x)\),其中 \(u,v\) 表示这条边的起点终点,\(w\) 表示长度,\(x\) 表示这条边的权重。图可能有重边。

开始时你在 \(1\) 号点,每当你到达一个点的时候,你会按照该点出边的【权重】随机选一条边走过去。具体来说,对于一个点 \(u\),假如有 \(k\) 条出边,它们的权重分别为 \(x_1,x_2,\dots x_k\),那么选择第 \(i\) 条边的概率就是 \(\frac{x_i}{\sum\limits_{i=1}^k x_i}\)。你的目标是找到一条 \(1\to t\)、总长度不超过 \(L\) 的路径,且满足终点 \(t\) 出度为 \(0\)

特别地,你可以在到达任意点时选择【重开】,即回到 \(1\) 号点并重新开始,且无次数限制。请你计算在达成目标时、走过的路径总长度的最小期望值(路径总长度包含历次重开走过的路径长度)。

保证答案存在,即在你运气足够好的情况下,一定可以达成目标。

数据范围:\(2\le n\le 100\)\(1\le m\le 200\)\(1\le w,x\le 100\)\(0\le L\le10^9\),保证输入的图无环,保证答案 \(\le10^9\),保证至少存在一条从 \(1\) 出发走到出度为 \(0\) 的点,且长度 \(\le L\) 的路径。

首先可以设一个 DP:\(dp_{u,l}\) 表示走到 \(u\),还剩下 \(l\) 步可以走的达成目标的最小期望值。

转移:\(dp_{u,l}\leftarrow \frac{x_{u,v}}{sumx_u}(dp_{v,l-w_{u,v}}+w_{u,v})\)

如果有重开,那么就是在当前期望不如重开的情况下选择重开,即 \(dp_{u,l}\leftarrow\min(dp_{u,l},dp_{1,L})\)

发现可以二分 \(dp_{1,L}\) 的值,将进行完 DP 后推出的 \(dp_{1,L}\) 与二分的值进行比较。这个具有单调性所以可以二分。

应该算是一道神仙题吧。

2.27

「ABC238F」Two Exams(二维偏序 + DP)

题面

题意:

\(N\) 个城市,每个城市有两个属性 \(p_i\)\(q_i\),保证 \(\{p_i\}\)\(\{q_i\}\) 是一个 \(1\sim n\) 的排列。

你要选择 \(K\) 个城市,满足 不存在 两个城市 \(x\)\(y\),使得:

  • \(x\) 被选,\(y\) 没被选,且 \(p_x>p_y\)\(q_x>q_y\)

问满足条件的方案数 \(\mod 998244353\) 的结果。

数据范围:\(1\le N\le300\)\(1\le K\le N\)

看到这个条件,很容易想到二维偏序。

处理偏序问题的常见方法之一:排序。

于是我们将 \(p\) 排序,记一个数组 \(v_i\) 表示在 \(p\) 中排名为 \(i\) 的城市在 \(q\) 中的排名,即 \(v_{p_i}=q_i\)

很明显,如果我们直接正着扫一遍 \(v\) 数组,\(p\) 的限制就已经被消除了,接下来就只要考虑 \(q\) 的限制。

考虑用 DP 解决这个问题。

\(dp_{i,j,k}\) 表示考虑了 \(v\) 数组的前 \(i\) 个数,选了 \(j\) 个,没有选的城市 \(q\) 的最小值为 \(k\) 的方案数。

转移枚举当前数选不选:

  • 如果 \(v_i<k\)\(j<K\),那么这个数就可以选,即 \(dp_{i,j,k}\leftarrow dp_{i,j,k}+dp_{i-1,j,k}\)
  • 否则不选这个数,\(dp_{i,j,\min(k,v_i)}\leftarrow dp_{i,j,\min(k,v_i)}+dp_{i-1,j,k}\)

当然还可以选择滚动数组进一步优化。

Code:https://atcoder.jp/contests/abc238/submissions/29751808

2.28

「ARC136D」Without Carry(高维前缀和)

题面

题意:

给定 \(n\) 个数 \(a_1,a_2,\dots,a_n\),问有多少对 \((i,j)\) 满足 \(a_i+a_j\) 没有进位。

数据范围:\(n\le 10^6\)\(0\le a_i<10^6\)

这道题告诉我们,高维前缀和也可以拓展到十进制。

\(dp_i\) 表示每一位上的值都 \(\le i\) 这一位上的值的数的个数。

转移就一位一位看,假设当前扫到了第 \(L\) 位,那么如果 \(j\) 在第 \(L\) 位上是 \(1\),那么 \(j-10^L\) 就转移到 \(j\)

Code:https://atcoder.jp/contests/arc136/submissions/29764020

「ARC136C」Circular Addition(结论)

题面

题意:

给一个由 \(n\) 个数 \(a_1,a_2,\dots,a_n\) 组成的环和一个长度为 \(n\) 的全是 \(0\) 的环,问最少能进行多少次下面的操作才能把全是 \(0\) 的环变成 \(a_1,a_2,\dots,a_n\)

  • 操作:选择环上的一个区间 \([l,r]\),将区间内的数 \(+1\)

数据范围:\(n\le2\times10^5\)\(1\le a_i\le10^9\)

神仙题。

把操作反过来,就变成了区间减。

\(M=\max(a_i)\)\(D=\sum\limits_{i=1}^{n-1}|a_{i+1}-a_i|+|a_n-a_1|\)。容易发现每次最多将 \(M\) 减去 \(1\) 或将 \(D\) 减去 \(2\)

那么答案就是 \(\max(M,\frac{D}{2})\)

下面假设 \(D\leftarrow\frac{D}{2}\)

证明:

  • \(M>D\),容易发现不可能存在 \(a_i=0\)\(0,\dots,M,\dots,0\) 这样 \(D\ge M\)),那么直接将整个环 \(-1\)\(M\) 就会 \(-1\)
  • \(M<D\),那么找到最大的数,将其 \(-1\)\(D\) 就会 \(-1\)
  • \(M=D\),如果 \(M\) 都分布在连续的一段,将这一段全部 \(-1\) 即可;否则选择最小的一段包含所有 \(M\) 的区间(也不可能存在 \(a_i=0\)\(0,\dots,M,\dots,M-k,\dots,M,\dots,0\) 这样 \(D>M\)),将其 \(-1\)。这样 \(M\)\(D\) 都会 \(-1\)

我们证明了这个下界,直接输出即可。

Code:https://atcoder.jp/contests/arc136/submissions/29764954

「ARC129C」Multiple of 7(构造)

题面

题意:

给定正整数 \(n\)

构造一个长度不超过 \(10^6\) 的字符串,使得其中满足 从左到右依次拼接起来,得到的十进制数是 \(7\) 的倍数 的区间 \([l,r]\) 恰有 \(n\) 个。

数据范围:\(n\le10^6\)

nb 构造题。

看到区间被某个数整除,就要快速想到转化成前缀和 / 后缀和的形式。

\(a_i\) 表示 \([i,|S|]\) 表示的数 \(\mod7\) 的余数。那么 \([l,r]\) 满足条件当且仅当 \(\frac{a_l-a_{r+1}}{10^{|S|-r}}\equiv 0\pmod 7\)

由于 \(10\)\(7\) 互质,那么条件等价于 \(a_l=a_{r+1}\)

那么我们可以得出:答案 \(=\sum\limits_{i=0}^6\frac{cnt_i(cnt_i-1)}{2}\)

然后对 \(n\) 进行三角形数拆分,可以发现 \(10^6\) 内贪心地能取就取最多取不超过 \(7\) 个三角形数。

那么把 \(a_i\) 相等的放在一起构造即可。

Code:https://atcoder.jp/contests/arc129/submissions/29773483

「CF936D」World of Tank(*3000,DP)

题面

题意:

坦克要从最左边走到最右边,但碰到障碍会爆炸。

每秒向右移动一步,然后可以选择是否上下移动。

坦克上有炮弹,每 \(t\) 秒可以发射一次,发射后立刻摧毁坦克右边第一个障碍(第 \(t\) 秒开
始才能发射)。

求能走到最右边的方案。

数据范围:\(n\le 10^9\),障碍数量 \(\le 10^6\)

考虑没有换行操作的情况,此时能量一累计到 \(t\) 就立刻释放一定是最优的,这也相当于可以无限积累能量,遇到障碍物时就倒退回上一次发射后 \(t\) 秒然后在这个时间释放能量。

因此对于原问题,在换行后就不能倒退回换行前的时间发射,因此换行时最多携带 \(t\) 的能量继续前进。设 \(f_{i,j}\) 表示到了 \((i,j)\) 所能积累的最大能量,则:

\[f_{i,j}=\left\{ \begin{aligned} f_{i-1,j}+1 & &a_{i,j}=0\\ f_{i-1,j}-t+1& &a_{i,j}=1\ \&\ f_{i-1,j}\ge t \end{aligned} \right. \]

其中 \(a_{i,j}\) 表示 \((i,j)\) 是否有障碍。

对于交换操作:\(f_{i,j}=\min(t,f_{i,3-j})\)

这样就可以 \(\mathcal{O}(n)\) 做了。

注意到若 \(x\)\(y\) 列有障碍且 \(x+1\sim y-1\) 列都没有障碍,那么最优方案一定是在 \(x+1\) 行直接换行。因此我们只需要关心有障碍的一列和下一列,离散化即可。

Code:https://codeforces.live/problemset/submission/936/147948431

3.1

3.1 考试 T1 与运算(高维前缀和)

题意:

给定一个序列,要求将其重新排序,使得所有前缀与的和尽量大。

数据范围:\(1\le n,a_i\le 10^6\)

考虑一个性质:对于一个数 \(x\),如果存在一个数 \(y\) 满足 \(y\) 二进制表示的每一位都大于等于 \(x\) 的这一位,那么 \(y\) 填在 \(x\) 前面肯定不劣。

\(f_i\) 表示填到 \(i\) 的答案,\(cnt_i\) 表示二进制表示包含 \(i\) 的数的个数。

那么就有转移:\(f_i=\max\limits_{i\subset j}\{f_j+i\times(cnt_i-cnt_j)\}\)

只需要枚举哪一位不同即可。

注意高维后缀和写法:

for (int j = 0; j <= 20; j+=1)
    for (int i = 2000000; i; i-=1)
        if (!(i >> j & 1))
            f[i].se += f[i ^ (1 << j)].se;

高维前缀和只需要反过来加即可。

Code:https://paste.ubuntu.com/p/c4jxhhWXrG/

3.1 考试 T2 序列(DP)

题意:

随机生成一个数列,每项有 \(p_i\%\) 的概率是上一项 \(+1\),有 \(1-p\%\) 的概率有 \(0\)。求和的平方的期望。

数据范围:\(1\le n\le 2\times10^6\)\(0\le p_i\le100\)

一个性质:\(E[(x+y)^2]=E(x^2)+yE(x)+y^2\)

\(f_i\) 为第 \(i\) 个数为 \(0\) 的情况下和的平方的期望,\(g_i\) 为和的期望。

转移:

\[g_i=\sum\limits_{j<i}(g_j+sum(1, i-j-1))\times(1-p_j)\prod\limits_{k=j+1}^{i-1}p_k\\ f_i=\sum\limits_{j<i}(f_j+g_j\times sum(1,i-j-1)+sum(1,i-j-1)^2)\times(1-p_j)\prod\limits_{k=j+1}^{i-1}p_k \]

后面的一项可以递推的时候直接算出来,这样就能 \(\mathcal{O}(n^2)\) 了。

拆开式子之后发现只要维护与 \(j\) 有关的项,递推过去即可。

Code(\(\mathcal{O}(n^2)\)):https://paste.ubuntu.com/p/VPW4nzcZYP/

「CF1276D」Tree Elimination(*2900,DP)

题面

这个 DP 是真不好想……

\(f_{i,0/1/2/3}\) 为在考虑 \(i\) 与父亲的连边时,\(i\) 的标记在考虑之前清除 / 在考虑时清除 / 在考虑之后清除 / 永不清除,\(fa_i\)\(i\) 与父亲连边的编号。

考虑转移:

  • 对于 \(f_{i,0}\),要在所有编号 \(<fa_i\) 的边中选择一个把这个点的标记删掉,设为点 \(j\),那么 \(j\) 之前考虑的点 \(v\) 要在 \((v,i)\) 考虑之前或者考虑后清除,之后的点不能在考虑时清除(因为 \(i\) 的标记已经没了),即 :\(f_{i,0}=\sum\limits_{fa_j<fa_i}(\prod\limits_{fa_v<fa_j}(f_{v,0}+f_{v,1})\times(f_{j,2}+f_{j,3})\times\prod\limits_{fa_u>fa_j}(f_{u,0}+f_{u,2}+f_{u,3}))\)
  • 对于 \(f_{i,1}\),考虑 \(fa_i\) 之前不能清掉标记,考虑之后标记被清除,即:\(f_{i,1}=\prod\limits_{fa_j<fa_i}(f_{j,0}+f_{j,1})\times\prod\limits_{fa_k>fa_i}(f_{k,0}+f_{k,2}+f_{k,3})\)
  • 对于 \(f_{i,2}\),与 \(f_{i,0}\) 相似,只是要在编号 \(>fa_i\) 的边中选一个点删掉;
  • 对于 \(f_{i,3}\),可以直接算,即:\(f_{i,3}=\prod\limits_{v\in son_i}(f_{v,0}+f_{v,1})\)

边界就是叶子节点 \(f_{i,1}=f_{i,3}=1\)

记录前缀 / 后缀积即可转移。

实现代码时注意,对于这种与所有儿子都有关的 DP,要先把子树 DP 完再进行当前节点的 DP。

Code:https://codeforc.es/problemset/submission/1276/148035298

3.2

「洛谷 P4755」Beautiful Pair(最值分治 + 主席树)

题面

看到与区间最大值有关的计数,马上想到最值分治。

设当前区间 \([l,r]\) 的最大值在 \(mid\) 的位置上,则先递归 \([l,mid-1]\)\([mid+1,r]\),再考虑跨过 \(mid\) 的区间贡献。

\([l,mid]\)\([mid,r]\) 较短的一个拿出来扫,在主席树上查询另一个区间中 \(\le\lfloor\frac{a_{mid}}{a_i}\rfloor\) 的数的个数。

查最大值所在的位置用 ST 表,注意主席树查询 \(\le\) 某个数的个数的细节问题:应该先递归左区间,再在 \(x>mid\) 的情况下递归右区间。

Code:https://paste.ubuntu.com/p/5YJzkdS533/

「CF981E」Addition on Segments(*2200,线段树分治 + bitset)

题面

不妨认为选取的操作子集中必须包含同一个位置。那么最大值就变成了操作的权值和。

那么暴力做法就是:枚举必须包含的位置,对所有包含该位置的操作的权值做一次背包,那么答案就可以为背包中这些数。

不难发现上述过程可以用线段树分治优化,背包可以用 bitset 优化。总复杂度 \(\mathcal{O}(\frac{nq\log n}{w})\)

Code:https://codeforc.es/problemset/submission/981/148074080

「APIO2019」路灯(树套树)

题面

考虑一个二维矩形,行坐标表示左端点,列坐标表示右端点。

每次翻转会影响的是一个矩形,用 set 维护全 1 段即可快速找出这个矩形。

如果 \(i\) 时刻某个矩形由不连通变为连通了,则将该矩形 \(−i\),否则将该矩形 \(+i\)

相当于是一个矩形加单点查询问题,差分后变为单点加矩形查询,树套树维护即可。

Code:https://loj.ac/s/1395663

「CF1637F」Towers(*2500,贪心)

题面

比较奇妙的贪心。

首先注意到只会在叶子节点上建塔。

我们以 \(h\) 值最大的为根,那么就至少要在两个子树里有 \(h_{rt}\),对于其他点,只要在子树内存在不小于自身 \(h\) 值的就可以了。

于是我们遍历全树,每次在子树内贪心地选择目前最大的已填的 \(h\) 值,如果不小于自身 \(h\) 值就不用管了,否则将其改为自身 \(h\) 值。对于根,我们选择最大和次大就好了。

Code:https://codeforc.es/problemset/submission/1637/148097948

Educational Codeforces Round 123 (Rated for Div. 2) A~E

link:https://www.cnblogs.com/xsl19/p/edu123_sol.html

开 VP 似乎蛮有意思的。。。

「ABC219G」Propagation(根号分治)

题面

题意:

给出一个有 \(n\) 个结点 \(m\) 条边的无向简单图,第 \(i\) 个结点初始时值为 \(i\) ,给出 \(q\) 次询问:

  • x :将与结点 \(x\) 相邻的点赋为 \(x\) 现在的值。

输出最后每个结点的值。

数据范围:\(1\le n\le2\times10^5\)\(0\le m\le \min(2\times10^5,\frac{n(n−1)}{2})\)\(1\le q\le2\times10^5\)

看到数据范围和操作就可以想到根号分治。

有两种暴力:暴力修改,直接查询 / 打标记修改,暴力查询。

考虑把两种暴力拼起来,设一个阈值 \(B\)

  • 如果操作的节点度数 \(\le B\),那么直接暴力修改;
  • 如果操作的节点度数 \(>B\),那么打上标记即可。

在更新一个节点的时候,用它相邻的度数 \(>B\) 的节点(个数不超过 \(\frac{2m}{B}\) 个)的标记暴力更新,最后输出答案的时候也做一遍。

为了平衡复杂度,把 \(B\) 设成 \(\sqrt{2m}\) 最优。

Code:https://atcoder.jp/contests/abc219/submissions/29812476

3.3

「ABC241F」Skate(图论)

题面

题意:

有一个 \(H\)\(W\) 列的溜冰场,上面有 \(N\) 个障碍。有一个人在 \((s_x,s_y)\) 处,想滑到 \((g_x,g_y)\) 处。每次滑冰可以选择一个方向,然后滑到这个方向最近的障碍物前一个位置。注意不能滑出溜冰场。问最少几次滑冰才能到达终点。

数据范围:\(1\le H,W\le10^9\)\(1\le N\le10^5\)

没什么好说的,先把障碍所在的行列离散化,然后暴力建图跑 BFS 就行了。

Code:https://atcoder.jp/contests/abc241/submissions/29817114

Good Bye 2021: 2022 is NEAR A~E

link:https://www.cnblogs.com/xsl19/p/goodbye2021_sol.html

「CF1316E」Team Building(*2300,状压 DP)

题面

将所有人按照 \(a_i\) 从大到小排序。

显然要选的观众一定是去掉队员后的一个前缀。

观察到 \(p\) 很小,考虑状压 DP。

\(f_{i, s}\) 表示扫到第 \(i\) 个人,已经选好的位置是 \(s\) 的最大力量值,额外记一个状态 \(zz_{i,s}\) 表示这段前缀的右端点(似乎可以直接算出来)

大力转移即可。

Code:https://codeforc.es/problemset/submission/1316/148171695

「CF825E」Minimal Labels(*2300,拓扑排序 + 堆)

题面

类似「HNOI2015」菜肴制作 的思路,建反图后拓扑排序,把大的 \(label\) 分配给编号大的点。

可以通过反证法证明这是对的。

Code:https://codeforc.es/problemset/submission/825/148173044

「CF1270G」Subset with Zero Sum(*2700,构造)

题面

注意到条件等价于 \(1\le i-a_i\le n\)

那么只要连边 \(i\to i-a_i\),那么图中就一定有至少一个环。环上的数和肯定为 \(0\)

Code:https://codeforc.es/problemset/submission/1270/148177556

「CF1348E」Phoenix and Berries(*2400,DP)

题面

一道思路比较新奇的题目。

\(suma=\sum a_i\)\(sumb=\sum b_i\),那么答案下界就是 \(\lfloor\frac{suma}{k}\rfloor + \lfloor\frac{sumb}{k}\rfloor\)

同样也可以得出答案上界是 \(\lfloor\frac{suma+sumb}{k}\rfloor\)

这样我们就发现了一个绝妙的结论:下界 \(\le\) 上界 \(\le\) 下界 \(+1\)

如果上界 \(=\) 下界,那么直接输出即可;否则可能需要通过在同一棵树上摘梅子来得到上界。

有一个朴素的 DP:设 \(f_{i,a,b}\) 表示前 \(i\) 棵树摘 \(a\) 个红梅和 \(b\) 个蓝莓是否可行。

由于剩下的梅子多于 \(k\) 个肯定不优,于是我们换一种想法:设 \(f_{i,a,b}\) 表示前 \(i\) 棵树摘的红梅 \(\% k=a\),摘的蓝莓 \(\%k=b\) 是否可行。我们发现始终有 \(a+b=k\),所以可以把 \(b\) 这一维省掉。

\(ka=suma\%k\)\(kb=sumb\%k\),那么如果 \(f_{i,a}(a\in[k-kb,ka])\) 有一个值为真,那么就可以取到上界。

这题太神了,只能膜拜 /bx

Code:https://codeforc.es/problemset/submission/1348/148191383

「ABC236G」Good Vertices(图上问题的矩阵乘法)

题面

题意:

给一张 \(n\) 个点的有向图,初始没有边,第 \(i(1\le i\le m)\) 时刻会加入一条边 \((u_i,v_i)\)

称一个点为“好点”,当且仅当从 \(1\) 出发能恰好走 \(L\) 步到达它。

对于每一个节点,问最早在什么时刻它是“好点”。如果它在 \(m\) 时刻都不是好点就输出 -1

数据范围:\(2\le n\le100\)\(1\le m\le n^2\)\(1\le L\le10^9\)

考虑对图的邻接矩阵进行操作。

这道题中,将矩阵乘法定义为 \((\min,\max)\),对原矩阵求 \(L\) 次幂,这样就能求出某个点最早在哪一个时刻是“好点”。原理应该比较简单。

Code:https://atcoder.jp/contests/abc236/submissions/29826864

3.4

Codeforces Global Round 18 A~F

link:https://www.cnblogs.com/xsl19/p/cgr18_sol.html

「JSOI2008」Blue Mary 开公司(李超线段树)

题面

算是李超线段树的模板题吧。

支持插入一次函数,查询某个位置上的极值点。

重新学了一遍这个东西,感觉还是比较好理解的:对于每个区间,维护在这个区间内最高的线段(可以感性理解一下)。每次插入的时候强制让新线段在 \(mid\) 上的取值比老线段大(不满足就 swap),然后如果左端点老线段更大就递归左边,右端点老线段更大就递归右边。每次只会递归一边,所以复杂度是 \(\log\) 的。

感谢 zhs 的板子 /bx

Code:https://paste.ubuntu.com/p/fT5MmZXj8d/

「HEOI2013」Segment(李超线段树)

题面

李超线段树插入线段的时候要先找到区间再进行插入,所以复杂度是 \(\log^2\)

Code:https://paste.ubuntu.com/p/GBHhgfYnjX/

「CF932F」Escape Through Leaf(*2700,李超线段树合并)

题面

一个很显然的 DP:设 \(f_i\) 表示 \(i\) 跳到一个叶子费用之和的最小值。

转移:\(f_i=\min\limits_{v\in sub_i}\{f_v+a_i\times b_v\}\)

容易发现这是一个一次函数的形式,李超线段树合并即可。

合并其实就是将一个线段插入到另一棵树上的同一节点里。

Code:https://paste.ubuntu.com/p/dKyMq2sqyR/

「SDOI2016」游戏(李超线段树区间修改 + 区间查询)

题面

把路径看成 \(s\to LCA\)\(LCA\to t\) 两段。

前一段的函数:\(a\times(dis_s-dis_i)+b=-a\times dis_i+(b+a\times dis_s)\)

后一段的函数:\(a\times(dis_s-dis_{LCA}+dis_i-dis_{LCA})+b=a\times dis_i+(b+a\times(-2\times dis_{LCA}+dis_s))\)

由于区间内一次函数最值只会在两端取到,因此在李超线段树上额外维护一个 \(\min\) 值,每次遍历到这个节点就更新一遍 \(\min\) 值。参考代码的写法可能更容易理解。

树剖后李超线段树区间修改、区间查询最小值即可。

Code:https://paste.ubuntu.com/p/3twDS5xhnJ/

3.5

3.5 考试 T2 难题走三方(DP)

题意:

有一个长度为 \(n\) 的排列 \(a\),进行不超过 \(K\) 次操作,每次选择一个区间 \([l,r]\),算出 \(mx=\max\limits_{i-l}^r a_i\),再对所有 \(l\le i\le r\) 执行 \(a_i=mx\),问最后可以得到多少种不同的排列。答案对 \(998244353\) 取模。

数据范围:\(1\le n\le 400\)\(1\le k\le 400\)

先考虑 \(a_i=i\) 的情况。

从左往右扫每一个数,可以发现每次操作可以选一个数,然后向左扩展,但是不能超过上一次操作的数最左边的那一个(否则上一次相当于没有操作)。

于是这样就可以 DP 了:设 \(f_{i,j,k}\) 表示扫到了第 \(i\) 个数,上一次操作最左边是 \(j\),操作了 \(k\) 次可以得到的数列数量。转移就是 \(f_{i,j,k}=f_{i-1,j,k}+\sum\limits_{0\le l<j}f_{i-1,l,k-1}\),前缀和优化转移即可。

考虑将上述做法扩展到一般情况。

还有一个性质:如果两个数 \(x\)\(y\) 满足 \(x<y\),那么最终数列的所有 \(a_x\) 都在所有 \(a_y\) 的左边。这是因为无论如何它们都不能交换彼此的相对位置,即偏序关系不变。

那么我们可以先求出来每一个数作为最大值的区间 \([l_i,r_i]\),则 \(a_i\) 只会在这个范围内出现。

于是我们用类似的思路 DP:设 \(f_{i,j,k}\) 表示考虑完前 \(i\) 个数,已经确定了最终序列的前 \(j\) 项,操作了 \(k\) 可以得到的数列数量。

一种看起来比较正确的转移:\(f_{i,j,k}=f_{i-1,j,k}+[l_i\le j\le r_j]\times\sum\limits_{d=l_i-1}^{j-1}f_{i-1,d,k-1}\)

但是这种转移在 \(i=j\) 的时候会出错,原因是这个时候 \(i\) 这个位置和没有操作的时候没有区别,所以要特判一下 \(f_{i,i,k}=f_{i-1,i,k}+\sum\limits_{d=l_i-1}^{i-2}f_{i-1,d,k-1}+f_{i-1,i-1,k}\)

边界要注意 \(f_{i,i,0}=1\)

Code:https://paste.ubuntu.com/p/tHRZt8B8BZ/

「ABC242F」Black and White Rooks(组合计数 + 容斥)

题面

题意:

有一个 \(n\times m\) 的棋盘,你要在上面放 \(b\) 个黑色的车和 \(w\) 个白色的车,使得异色的车不互相攻击,问方案数 \(\mod 998244353\) 的结果。

数据范围;\(1\le n,m\le 50\)\(1\le b,w\le 2500\)\(b+w\le n\times m\)

考虑一个子问题:在 \(n\times m\) 的矩形里随便放 \(x\) 个车的方案数。设答案为 \(f_{n,m,x}\)

可以用容斥解决:枚举有多少行列放了车,那么就有:\(f_{n,m,x}=\sum\limits_{i=1}^n\sum\limits_{j=1}^m(-1)^{n+m-i-j}\binom{n}{i}\binom{m}{j}\binom{i\times j}{x}\)

题目中的条件其实就相当于黑色的车和白色的车所放的矩形没有交(这句话感性理解一下)。

那么枚举黑色和白色的车分别所在的矩形大小,答案就是:

\[\sum\limits_{i=1}^n\sum\limits_{j=1}^{n-i}\sum\limits_{k=1}^m\sum\limits_{l=1}^{m-k}\binom{n}{i}\binom{n-i}{j}\binom{m}{k}\binom{m-k}{l}f_{i,k,b}\times f_{j,l,w} \]

Code:https://paste.ubuntu.com/p/B42hqxhKdg/

「USACO22FEB」Moo Network G(Borüvka 算法 + set)

题面

对每一行开一个 set,然后 Borüvka 求出 MST 即可。

Code:https://paste.ubuntu.com/p/8TnxvgN7mx/

3.7

3.7 考试 T3 白色夫人的根 / 「CF768G」The Winds of Winter(*3300,分类讨论 + set)

题意:

给出一棵 \(n\) 个点的有根树,删掉一个节点后做零次或一次操作,即可以把一个连通块里的一棵子树接到另一个节点上(不能是已经删除的节点),问删掉每个节点后所得到的最大连通块大小最小是多少。

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

首先求出删掉每个节点后的最大 / 次大 / 最小连通块,容易发现如果要操作肯定是要在最大连通块里操作,放到最小的子树里,而且操作的子树大小要尽量接近 \(\frac{\operatorname{maxsize-minsize}}{2}\)

假设当前点为 \(u\),分情况讨论:

  • 若最大连通块是 \(u\) 的一棵子树,那么对每一个节点开一个 set 记录子树内所有子树的 size,求答案直接在重儿子的 set 里 lower_bound 就行。

  • 若最大连通块是 \(u\) 的父亲的连通块,则可以操作的 size 实际上就是 \(u\) 到根的链上节点的其它子树 size 和 \(u\) 到根的链上节点的 size 减去 \(size_u\)。前者可以通过正反两遍 dfs 求出,后者直接记录一下推推式子即可。

    • 具体的,dfs 的过程如下所示,其中一遍遍历子树是 vector 从头到尾遍历,一遍是从尾到头:

      dfs(u)
          查询 u
          遍历 u 的子树
          加入 size_u
      
    • 推式子大概就是 \(size_x-size_u=ans\to size_x=ans+size_u\),那么 lower_bound 的时候加上 \(size_u\) 这个偏移量就行。

Code:https://paste.ubuntu.com/p/9nkQkrKrRg/

3.8

「ABC233G」Strongest Takahashi(DP)&「ABC233H」Manhattan Christmas Tree(曼哈顿距离转切比雪夫距离 + 二维树状数组 / 主席树)

link:https://www.cnblogs.com/xsl19/p/abc233_g_h_sol.html

「ABC234G」Divide a Sequence(单调栈优化 DP)&「ABC234H」Enumerate Pairs(k-D Tree)

link:https://www.cnblogs.com/xsl19/p/abc234_g_h_sol.html

「USACO22FEB」Cow Camp G(DP + 二分 + 矩阵乘法)

题面

样例根本没啥用,所以一开始就 \(T\leftarrow T-1\)

\(f_i\) 为前 \(i\) 次提交的期望得分。

转移考虑再提交一次会不会增加期望得分,即:

\[f_i=\begin{cases} \frac{n}{2} &i=1\\ f_{i-1}\times\sum\limits_{j=0}^{\lfloor f_{i-1}\rfloor}\frac{\binom{T}{j}}{2^T}+\sum\limits_{j=\lceil f_{i-1}\rceil}^T \frac{j\times\binom{T}{j}}{2^T} &i>1 \end{cases} \]

直接暴力可以 \(\mathcal{O}(k)\)

发现转移和 \(f\) 的下取整有关,这个值不超过 \(T\),于是把 \(f\) 下取整相同的放在一起用矩乘转移即可。求要转移多少次可以用二分。

Code:https://paste.ubuntu.com/p/6xz3wGqwcb/

3.9

「AGC006F」Blackout(性质题 + 图染色)

题面

题意

把问题转化成图论问题:若有边 \((x,y)\)\((y,z)\),那么连边 \((z,x)\),问最后有多少条边。

发现问题与三元环有关系,于是考虑对图进行三染色,即红点都向绿点连边,绿点都向蓝点连边,蓝点都向红点连边。

对最后的染色情况进行讨论:

  • 若染色出现矛盾,则图最后会连成完全图。
  • 若颜色用不完,则肯定不会新连边,即边数不变。
  • 若能够完成染色且颜色用完了,则最后肯定所有红点与所有绿点都有连边,所有绿点和蓝点、所有蓝点与红点也都有连边,即边数为 \(cntr\times cntg+cntg\times cntb+cntb\times cntr\)

参考证明:https://www.luogu.com.cn/blog/sry/solution-at217

Code:https://atcoder.jp/contests/agc006/submissions/29972208

3.10

「ABC231G」Balls in Boxes(期望推式子 + DP)&「ABC232G」Modulo Shortest Path(优化建图 + 最短路)

link:https://www.cnblogs.com/xsl19/p/abc231g_abc232g_sol.html

「CF1286D」LCC(*3100,线段树维护 DP)

题面

不难发现碰撞只会在相邻两个粒子中发生。

把所有碰撞的情况找出来,按照时间从小到大排序,第 \(i\) 种碰撞的情况是第一次碰撞当且仅当前 \(i-1\) 种情况没有发生且第 \(i\) 种情况发生。也就是说,每一种碰撞都会禁掉一种方向选择的情况,若前 \(i-1\) 种情况和第 \(i\) 种的其它三种情况都禁掉了,那么它就是第一次碰撞。

这样可以推出来一种 DP 做法:设 \(f_{i,0/1}\) 为已经考虑了前 \(i\) 个粒子,第 \(i\) 个粒子向右 / 向左的概率,转移的时候不转移已经禁掉的情况就行。直接暴力做是 \(\mathcal{O}(n^2)\) 的。

考虑如何优化,发现每次我们只会增加一种限制情况,很自然地想到每次单点修改只会改 \(\mathcal{O}(\log)\) 个点的线段树。

具体的,线段树上每个节点维护 \(val_{p,0/1,0/1}\) 表示左端点向左 / 向右,右端点向左 / 向右且满足条件的概率。修改就直接从叶子节点往上跳一路 pushup,pushup 只要考虑中点的情况。

Code:https://codeforc.es/problemset/submission/1286/149038465

「洛谷 P1502」窗口的星星(查询 / 状态互补 + 扫描线)

题面

一个朴素的想法是枚举左上角,统计矩形里星星亮度总和。一个点被算入贡献的条件为它在矩形中。

这样并不好算,于是考虑状态互补:以每个点为右下角建立一个矩形,那么一个点被算入贡献的条件为选择矩形的左上角在以它为右下角的矩形中。这样就好算了许多,直接扫描线算权值最大的点即可。

由于边框上的点不算,那么直接一开始将长宽分别减去 \(1\) 再统计就行了。

Code:https://pastebin.ubuntu.com/p/cNxbgSVMNf/

「CEOI2017」Building Bridges(李超线段树)

题面

朴素 DP:设 \(f_i\) 为前 \(i\) 根柱子,\(1\)\(i\) 连通的最小代价。

转移:

\[\begin{aligned} f_i&=\min\limits_{j=1}^{i-1}\{f_j+sumw_i-sumw_{j}+(h_i-h_j)^2\}\\ &=sumw_i+h_i^2+\min\limits_{j=1}^{i-1}\{-2h_ih_j+f_j-sumw_j+h_j^2\} \end{aligned} \]

括号里的可以看成斜率为 \(-2h_j\),截距为 \(f_j-sumw_j+h_j^2\) 的一次函数在 \(h_i\) 上的最小值。

李超线段树即可。

Code:https://loj.ac/s/1404994

3.11

3.11 考试 T1 单次出现(套路题 + 线段树)

题面:

给定长度为 \(n\) 的序列 \(a_1,a_2,\dots,a_n\),求有多少个区间满足区间中恰有一个数出现了一次。

数据范围:\(1\le n\le 5\times10^5\)\(1\le a_i\le10^9\)

套路题,先求出每个数的前驱,对于一个区间只考虑每个数最后一次出现的位置,如果这个位置的前驱在左端点之前则计数器加一,若最后计数器值为 \(1\) 说明这个区间满足条件。

考虑如何维护这个东西,每次扫描线扫到一个数,就把 \((pre_i,i]\) 区间 \(+1\),并把 \((pre_{pre_i},pre_i]\) 区间 \(-1\),意思是说只有左端点在 \((pre_i,i]\) 这个区间里才会对计数器有 \(1\) 的贡献。

问题变成了线段树区间加、区间数 \(1\) 的个数。注意到一个位置不可能值变成负数,所以如果存在 \(1\) 那么 \(1\) 只会是最小值 / 次小值。因此线段树只要统计最小值 / 次小值个数即可。

Code:https://paste.ubuntu.com/p/9JG23zzPft/

3.11 考试 T2 完美覆盖(性质题 + DP)

题面:

给定 \(n \times n\) 棋盘,其中 \(n\) 是偶数。

\(1\times 2\) 长方形骨牌覆盖棋盘,每格可以覆盖或者不覆盖,但不准重叠或超出界外。

如果任意一个 \(2 \times 2\) 的子矩阵中,都没有两个不相邻的格子同时被骨牌覆盖,在此前提下骨牌的数量最多,就说这个覆盖是完美的。

现在已知一些格子含于某完美覆盖,求有多少种如此的完美覆盖。答案对 \(998244353\) 取模。

数据范围:\(2\le n\le2000\)

因为 \(n\) 为偶数,所以可以把棋盘划分成 \(\frac{n}{2}\times\frac{n}{2}\)\(2\times2\) 的子矩形。

性质:完美覆盖当且仅当每一个划分后 \(2\times2\) 的子矩形内都放了一个骨牌。

发现产生冲突只有可能是前一个棋盘的右上角和后一个棋盘的左下角 / 前一个棋盘的右下角和后一个棋盘的左上角,而这两种情况可以分开考虑。

对于第一种情况,如果把填左下角的格子单独拿出来,可以发现它们一定从最左端开始填,且右边界是递增的;对于第二种情况,如果把填右下角的格子单独拿出来,可以发现它们的、一定从最右端开始填,且左边界是递减的。

直接 DP 边界情况即可。

Code:https://paste.ubuntu.com/p/ZmrtYZHBwC/

3.13

「CF893F」Subtree Minimum Query(主席树)

题面

将所有点按照深度排序加入主席树,依次将点权放在对应的 dfs 序上,查询的时候只需要查 \(dep_x+k\) 的主席树上 dfs 序在区间 \([dfn_x,dfn_x+sz_x-1]\) 中的最小点权。因为 \(x\) 子树内的其它点此时都没有加入到主席树中。

Code:https://codeforc.es/problemset/submission/893/149487839

「BZOJ4771」七彩树(虚树差分 + set + 主席树)

题面

先不考虑深度的限制。对于每一种颜色,建出包括根的虚树,然后将在虚树边上的每一个点答案 \(+1\)

这里介绍一个虚树差分的技巧:将虚树上的每个点权值置为 \(1\),然后在 dfs 序相邻的两个点的 LCA 处权值 \(-1\),对于每一个点,它子树内所有点的权值和就是它的贡献。这样就把问题转化成了单点加和子树查。

可以认为在 LCA 处把两条路径合并成一条,所以要减去某一条的贡献,由于按 dfs 序考虑正确性同虚树。

回到原问题,对于深度的限制可以像上一道题那样按照深度加入点,对于每种颜色维护一个按照 dfs 序排序的 set,在虚树上动态加点,对于询问主席树子树查询即可。

Code:https://pastebin.ubuntu.com/p/qjsMrnD2Gs/

「JSOI2018」列队(主席树)

题面

肯定的是,在集合后学生的相对位置不会改变。

于是维护一棵主席树,每次将第 \(i\) 个学生的位置插入,维护区间学生个数和学生位置的编号和。

对于询问到的区间 \([l,r]\),有以下几种情况:

  • 区间内没有学生;
  • 区间内的学生全都往左跑;
  • 区间内的学生全都往右跑;
  • 区间内的学生有往左跑的,也有往右跑的;

\(1\) 种可以直接返回 \(0\),第 \(2\) 种和第 \(3\) 种可以用等差数列求和公式计算,第 \(4\) 种直接递归两个子树。

Code:https://loj.ac/s/1409101

3.14

3.14 考试 T1 上进心(数论 + BSGS)

题面:

qwq 公司体量巨大,共有 \(k\) 个工作小组。

\(k\) 个工作小组都充满了上进心,这体现在每天的工作量上:设 $c_{x,i} $为第 \(i\) 个小组第 \(x\) 天的工作量,那么在
\(x+1\) 天,第 \(i\) 个小组就会完成 \(1\sim i\) 个小组上一天工作量的总和的 \(t\) 倍,即 \(c_{x+1,i}=t\sum\limits_{j=1}^i c_{x,j}\)

除此之外,每过 \(m\) 天之后的月底,每个小组的组长会给当天小组额外加上 \(b_i\) 的工作量,即 \(c_{m,i}\leftarrow c_{m,i}+b_i\),并将这天的小组的工作量模 \(10^9+7\) 的答案记录在公司的小本本上,以作鼓励。

据 qwq123 所知,该公司成立于月底,当天第 \(i\) 个工作小组的工作量是 \(a_i\)(创立第一天不会有组长的额外工作量),之后每个月底组长的额外工作量都是 \(b_i\)

现在他在公司的小本本上看到了某月底留下来的各个小组的工作量记录 \(d_i\),想知道留下记录的这个月距离公司创办 至少 已经过去几个月了,如果答案 \(>10^{10}\),那么就可以看做答案不存在,输出 \(-1\)

数据保证若 \(a_1=d_1\),则第一个月月底的第一组的工作量和 \(d_1\) 在模 \(10^9+7\) 意义下不同。

数据范围:\(1\le k\le 20\)\(1\le m\le 10^8\)\(2\le t,a_i,b_i,t_i\le 10^9\)

提供一种比较暴力的做法。

首先可以用 BSGS 算出第 \(1\) 个小组在什么时候会变成 \(d_1\),然后可以肯定的是,在 \(2\) 次或 \(5\times10^8+3\) 次后又会变成 \(d_1\)(因为 \(\varphi(10^9+6)=2\times(5\times10^8+3)\))。

求若干次后每个小组的数字可以用矩乘。

Code:https://pastebin.ubuntu.com/p/3WjxrttJnQ/

3.14 考试 T2 欢乐感 / 「BalticOI 2015」拔河(二分图的性质 + bitset 二进制分组优化多重背包)

题面

首先可以肯定的是,每个位置上都要坐人。

考虑建图,连边 \((a_i,b_i+n,c_i)\)\((b_i+n,a_i,-c_i)\)

这样就可以把一些已经确定的位置判掉(即度数为 \(1\)),然后肯定剩下若干个偶环。

环上的权值肯定是正负相间的,而且也只有两种选法(即顺时针填和逆时针填),所以考虑先随便选一种,另一种的权值肯定是当前选法的相反数。

这样问题就变成了:给出当前权值 \(sum\),有 \(cnt_i\)\(i\) 可以加上,问 \(sum\) 的绝对值的最小值是否 \(\le k\)

这是一个典型的多重背包,bitset 二进制分组优化即可。

注意 bitset 右移位数大于其长度是 UB,所以应该在环上选的权值为负数,这样 \(i\) 就都是正数,变成了左移操作。

Code:https://loj.ac/s/1409982

「CF468C」Hack it!(构造)

题面

首先可以发现,对于 \(0\le x\le 10^{18}\)\(f(x+10^{18})=f(x)+1\)

\(\sum\limits_{i=0}^{10^{18}-1}f(i)\equiv p\pmod a\),则有 \(\sum\limits_{i=j}^{10^{18}-1+j}f(i)\equiv p+j\pmod a\)

那么可以构造 \(\sum\limits_{i=a-p}^{10^{18}-1+a-p}f(i)\equiv p+a-p\pmod a\)

不难发现 \(p=81\times10^{18}\),这样就能直接构造了。

Code:https://codeforc.es/problemset/submission/468/149606029

「WC2022」秃子酋长(回滚莫队 + 链表)

题面

看到这种多次区间询问就可以想到莫队。

普通莫队大概可以过 \(50\),拿个 set 维护就行。

发现这个问题插入还要查询前驱后继,至少要一个 \(\log\),但删除可以拿链表 \(\mathcal{O}(1)\) 维护,于是考虑不插入莫队。

和不删除莫队相反,我们先把所有元素放进当前集合,跳跃块的时候把当前块的数删掉。

处理每一个询问的时候直接跳就行,链表要支持撤销删除所以还要用一个栈。

细节:跳跃块的时候删除完还要将栈清空。

Code:https://pastebin.ubuntu.com/p/YwFsm7F6gc/

3.15

3.15 考试 T1 排列(剪枝 + DP + 打表)

题面:

Takahashi 君定义相邻元素互质的排列是好的,于是他给你 \(n, p\),让你求出长度为 \(n\) 的好的排列的个数 \(\bmod~p\)

数据范围:\(1\le n\le 28\)

发现质因子组成相同的数没有区别,根据这个将 \(1\sim 28\) 划分成 \(17\) 个等价类,然后暴力跑 DP 把表打出来即可。

打表程序:https://pastebin.ubuntu.com/p/MMFKhfTqpY/

Code:https://pastebin.ubuntu.com/p/6wffzpqQwp/

3.15 考试 T2 集合划分计数问题(结论 + DP)

题面:

Kahashi 君将 \(1 \sim n\) 分到 \(m\) 个集合中,满足以下条件:

  • 每个数恰好在每个集合中出现一次。
  • 每个集合中至少有 \(1\) 个数。
  • \(S_i\) 表示第 \(i\) 个集合中的数,那么存在一个排列 \(p_1 , p_2 , \dots, p_m\) 满足 \(\forall i \in [1, m], \max\{S_{p_i}\} > \min\{S_{p_i−1} \}\),其中 \(S_{p_0} = S_{p_m}\)

如果两个数在一种方案中被划分到了同一个集合,在另一种方案中划分到了不同的集合中,那么就称两个方案是本质不同的。现在,Kahashi 君想让你求出本质不同的划分方案数 \(\bmod 10^9+7\)

数据范围:\(1\le m\le n\le500\)

考虑一个划分时候是合法的,当且仅当:

不存在一个集合 \(s\),满足 \(\max_{i\in s} \{S_i \} < \min_{i\in s} \{S_i \}\)

首先,必要性是比较显然的,如果存在这么一个集合,那么肯定是如何划分都会有分界点而不可能满足条件。

可以通过构造方案来证明充分性。

接着考虑我们这个条件代表这什么,这个条件意味着如果我们把每个集合当做一个 \(\min \to\max\) 的线段,那么所有线段是联通的。

于是我们可以把 \(1 \sim n\) 依次确定其要分到每个集合,记 \(f (i, j, k)\) 表示当前我们 DP 到了第 \(i\) 个数,已经确定了 \(j\) 条线段的左端点,其中有 \(k\) 条线段的右端点没有确定的方案数,转移的时候可以枚举这个点作为 新线段的左端点 / 新线段的左右端点 / 一条线段的中间节点 / 一条线段的右端点。注意如果我们把这个点当做左端点,那么一定不能所有线段都确定了右端点。

Code:https://pastebin.ubuntu.com/p/2pFy62fYpH/

「洛谷 P4719」【模板】"动态 DP"&动态树分治(动态 DP)

题面

DDP 板子。

详解:https://www.cnblogs.com/alex-wei/p/DP_Involution.html

Code:https://pastebin.ubuntu.com/p/z3mCKh2FF9/

「NOIp2018 提高组」保卫王国(动态 DP)

题面

强制选 \(\to\) 权值设成 \(-INF\)

强制不选 \(\to\) 权值设成 \(INF\)

然后 DDP 板子。

Code:https://pastebin.ubuntu.com/p/k6nJRnjctH/

posted @ 2022-02-25 09:08  csxsi  阅读(12)  评论(0)    收藏  举报