Hey Gift:I don't wanna be an internet rapper
之前的做题记录,存一下。
AGC030D
转期望 DP 算出每个 \(a_i>a_j\) 的概率乘上 \(2^q\),不过考虑每一次交换都会有大部分的 \(a_i>a_j\) 的方案数量乘上 \(2\) 也能想到。神仙转化。
写的时候思考不能乱,不然会想复杂。
考虑 \(dp_{x,y}\) 表示 \(a_x>a_y\) 的概率,对于一次 swap \(x,y\) 的操作,只需要考虑所有 \(dp_{x,i},dp_{i,x},dp_{y,i},dp_{i,y}\)。转移如下:
fr1(i,1,n){
if(i==x||i==y){
continue;
}
dp[x][i]=dp[y][i]=(tmp[y][i]*inv2%M+tmp[x][i]*inv2%M)%M;
dp[i][x]=dp[i][y]=(tmp[i][y]*inv2%M+tmp[i][x]*inv2%M)%M;
}
dp[x][y]=(tmp[x][y]*inv2%M+tmp[y][x]*inv2%M)%M;
dp[y][x]=(tmp[x][y]*inv2%M+tmp[y][x]*inv2%M)%M;
非常浅显,\(tmp\) 表示操作前的 \(dp\) 数组。
CF626F
\(f_{i,j,k}\) 表示前 \(i\) 个有 \(j\) 个集合未闭合,不和谐度之和为 \(k\) 的方案数。
然后如果在一边小减一次大加一次值域有点烦人,不如在中间加着垒起来,所以差分得到 \(d_i=a_i-a_{i-1}\),然后有:
神仙优化。
ABC236E
没有人过的模拟赛防 AK 题。
一开始看到不大的误差以及贡献跟位数有关无法 dp 就要想到二分,然后判断某个数能否成为合法的平均数。假设二分 \(x\) 于是判断:
于是让 \(b_i=a_i-x\),dp 出满足条件的最大子序列即可。
中位数也是同理。二分 \(x\),将所有 \(a_i<x\) 的 \(i\) 构造 \(b_i=-1\),反之构造 \(b_i=1\),只要可以选一个序列 \(p\) 满足 \(\sum b_{p_i}>0\) 就说明可以。有点厉害。
TopCoder SRM478C
有 \(k\) 种苹果分装到 \(n\) 个箱子,先等概率选取一个箱子的非空子集,再在箱子中的苹果构成的苹果集中选择一个,问 \(\forall i\in [1,k]\) 有多少概率选到种类 \(i\) 的苹果。
\(1\leqslant n,k\leqslant 50\),每个箱子中的每个苹果不超过 \(199\) 个。
考虑每个箱子中的苹果带来的贡献。注意到同个箱子内的苹果对自己种类的贡献是相同的,所以考虑一个箱子内的某个苹果对自己种类的这个贡献是什么玩意。
假设 \(siz_i\) 表示箱子 \(i\) 里面苹果的总数,\(sum\) 表示总共有多少苹果。\(a_{i,j}\) 表示箱子 \(i\) 中苹果 \(j\) 的总数。
考虑一个箱子,假设这个箱子被选中,然后考虑其他箱子的状态。很明显其他箱子的每一种状态都对应一个选择这个箱子的状态。于是我们考虑枚举除了这个箱子以外的箱子有 \(j\) 个苹果,很明显 \(j\in[0,sum-siz_i]\)。
不妨设 \(f_{i,j}\) 表示除了箱子 \(i\) 有 \(j\) 个苹果的方案数,于是其他箱子的状态数就是 \(f_{i,j}\),然后总方案数很明显是 \(2^n-1\),于是选出这个箱子的概率是 \(\frac{f_{i,j}}{2^n-1}\)。所有这些箱子的总苹果数是 \(j+siz_i\),于是选中某个苹果的概率就是 \(\sum\limits_{j=0}^{sum-siz_i}\frac{f_{i,j}}{2^n-1}\times \frac{1}{j+siz_i}\)。
接下来考虑如何得到 \(f_{i,j}\)。先考虑怎么得到前 \(i\) 个箱子有 \(j\) 个苹果的方案数 \(g_{i,j}\),乱 dp 即可:
然后考虑对 \(f_{i,j}\) 的推法,很明显箱子 \(i\) 只能选或者不选,于是很容易想到:
可以写记忆化搜索,好。初值要赋值。
然后记得给 \(f,g\) 开 long long,而且 TopCoder 的 MLE 报 WA 所以要压维。
感觉没有那两个题难。不过很有意思!
P2518 [HAOI2010] 计数
其实不太难的,但是很有意思的转化。
首先我们可以把所有 \(0\) 去掉得到原可重数集 \(S\),令大小 \(|S|\) 为 \(n\)。
然后可以把 \(n\) 个 \(0\) 放入 \(S\) 得到 \(S'\),于是你可以发现 \(S'\) 的任意一个排列去掉前导零都是一个可以得到的数,并且是一一对应的。
于是你又可以保留所有前导零,并且把询问的数加上前导零,使得所有排列的长度统一为 \(2n\)。
因为长度一样,所以数的大小也就转换成了排列的字典序。
于是问题转换成可重集 \(S'\) 有多少个排列小于给定排列。
是很简单的数位 DP。非可重集的同个问题是 P5367 【模板】康托展开。
P4046 [JSOI2010] 快递服务
\(dp_{i,j,k}\) 表示 \(a\)到 \(s_i\),\(b\) 到 \(s_j\),\(c\) 到 \(s_k\) 的最小花费和。
考虑转移。
枚举位置 \(u\),然后转移:
这样做是 \(O(|s|^4)\) 的。
这状态很明显很垃圾。
\(dp_{i,j,k}\) 表示 \(a\) 到地点 \(i\),\(b\) 到地点 \(j\),\(c\) 到地点 \(k\) 的最小花费和。
考虑转移。
枚举序列中的位置 \(u\),得到:(其中一个例子)
这样做可以做到 \(O(|s|m^3)\)。
能不能把状态再简单一点!考试的时候只发现了 \(u\) 这个位置必然有一个司机,但是又可以发现,\(u-1\) 也一定有一位司机,所以我们考虑:
枚举 \(u\),\(dp_{i,j}\) 表示一位司机位于位置 \(i\),一位司机位于位置 \(j\),还有一位正在 \(u-1\)。然后转移:
注意取 \(\min\),因为 \(s_{u-1}\) 也会在 \(i,j\) 的枚举中被枚举到的。
非常神仙的状态!
P2470 [SCOI2007] 压缩
记 \(dp_{l,r,0/1}\) 表示区间 \([l,r]\) 中是否有 \(\texttt{M}\) 的最短压缩,原因是一个区间内部的 \(\texttt{M}\) 会对合并造成影响。
对于所有 \(dp_{l,r}\),我们假定在 \(l-1\) 的位置上一定有个 \(\texttt{M}\)。原因是 \(1\) 处确实默认有一个 \(\texttt{M}\),而一个有效的压缩状态也必须要保证最开头有个 \(\texttt{M}\) 去复制。
考虑 \(dp_{l,r,0}\) 的转移:
- 若 \([l,r]\) 可以拆分成相等的两半,在中间加上一个 \(\texttt{R}\) 就好了,也就是 \(dp_{l,r}\gets dp_{i,mid,0}+1\)。很明显左半边不能有 \(\texttt{M}\),不然中间这个 \(\texttt{R}\) 取不到 \(l-1\) 的 \(\texttt{M}\) 就不能复制左半边。
- 否则 \(dp_{l,r,0}\gets \min(dp_{l,r,0},dp_{l,k,0}+j-k)\),首先右半边肯定不能含有 \(\texttt{M}\),其次因为 \(k\) 和 \(k+1\) 之间没有 \(\texttt{M}\) 所以右半边不可能会被需要压缩,直接把原串拿过来就行。
考虑 \(dp_{l,r,1}\) 的转移:
我们枚举中间 \(\texttt{M}\) 的位置,然后转移:
其实中间有可能不需要 \(\texttt{M}\),但是这种情况一定说明 \([l,r]\) 中还有别的出现有用 \(\texttt{M}\) 的地方,它会在那些位置因为多出字符而被舍弃。
初始化就是 \(dp_{i,i,0}=1\),\(dp_{i,i,1}\) 可以赋值成任何大于 \(0\) 的值,因为它在对一个字符与一个字符拼接的情况下总是不会比 \(0\) 更优。
个人感觉很难想清楚!
P4448 [AHOI2018初中组] 球球的排列
这状态真的太神仙了吧。建议放到 NOI。
首先做一步转化,注意到 \(xy=p^2,xz=q^2\) 的时候存在 \(x^2yz=p^2q^2\Rightarrow yz=(\frac{pq}{x})^2\),很明显因为 \(pq\) 和 \(x\) 都是整数,并且 \(yz\) 也是整数,所以 \(yz\) 一定是完全平方数。
于是我们就可以把所有具有乘起来是完全平方数性质的数对并查集合并在一起,最后对于每一个并查集我们染同种颜色。然后通过排序使同种颜色挨在一起。
排序的意义是为了方便界定是否已经存在过该颜色的球。
我们的目的是要求任意两个相邻的球不同色。
然后这个时候你试图 dp 它,但是状态设不来。
\(dp_{i,j,k}\) 表示前 \(i\) 个球有 \(j\) 个与它颜色不同的挨在一起,有 \(k\) 个与它颜色相同的挨在一起。
剩下的部分只需要讨论新加球与上一个球的颜色、新加球的位置即可。复杂度 \(O(n^3)\)。
link:极好的题解!
多理解一下,牢记 dp 数组的含义,是可以理解神奇的转移的!需要牢记题解中所有的 \(j,k\) 枚举的都是加入 \(i\) 球之后的状态,之前的状态都用的是 \(j'\)!除此之外,对于转移 \(5\) 的减法,不能直接用 \(i\) 去减掉转移 \(3,4\) 的系数(例如转移 \(3\) 的 \(2cnt-(k-1)\) 在这里就应该是 \(2cnt-k\),因为在 \(5\) 中并没有增多一个与 \(i\) 同色的同色相邻球对。)
to do list:\(O(n^2)\) 容斥 / 生成函数做法。
P2160 [SHOI2007] 书柜的尺寸
考虑 \(dp_{i,j,k,p}\) 表示前 \(i\) 本书,第一层厚度为 \(j\),第二层厚度为 \(k\),第三层厚度为 \(p\),此时最小的书柜高度。这肯定过不掉。
注意到 \(p\) 可以被前面两个东西推出来,\(p=(\sum\limits_{q=1}^{i} t_q)-j-k\),然后可以把 \(p\) 搞掉,再开个滚动,这样时间和空间复杂度就都对了。
然后呢?每次插入一本书我不会更新答案啊!
那就把书从高到低排序,这样进入一层的第一本书一定带来了答案。
然后就做完了。
trick:当加入的贡献难以统计时,如果顺序没有影响,考虑排个序保证除了第一次加入的贡献是有效的,后面的贡献都是无效的。
P4072 [SDOI2016] 征途
令 \(l\) 表示分成 \(m\) 天之后每一天的路程和。
其中 \(sum\) 表示 \(n\) 段路程的长度前缀和。这题的难点就在于意识到最后一步转化。最后一步转化前是无法 dp 的,因为不能直接转移方差,在前面不够小的方差可能在加入一段之后变得非常小,前面的最小方差可能变得非常大,无法转移。
考虑 dp 出 \(\sum\limits l^2\) 的最小值,设 \(dp_{i,j}\) 表示前 \(i\) 段分成 \(j\) 天走的最小各天路程平方和。很容易得到式子:
可以得到 \(60\) 分。
不妨考虑使得 \(k'=2sum_i,y=dp_{k,j-1}+sum_k^2,x=sum_k\) 做斜率优化。
细节:交换 \(i,j\) 求和顺序,枚举 \(j\) 时维护 \(j-1\) 的下凸壳,还可以保证每次单调队列里面的线的 \(i\) 都比目前枚举的 \(i\) 要小。
P3299 [SDOI2013] 保护出题人
考虑每一次种的植物的攻击力需要满足怎样的不等式。
-
第一关:
\[y_1x_1\geqslant a_1 \] -
第二关:
\[\begin{cases} y_2x_2\geqslant a_2\\ y_2(x_2-\frac{a_2}{y_2}+d)\geqslant a_1\Rightarrow y_2x_2+dy_2\geqslant a_1+a_2 \end{cases} \]很明显是两个式子。
-
第三关:
\[\begin{cases} y_3x_3\geqslant a_3\\ y_3(x_3-\frac{a_3}{y_3}+d)\geqslant a_2\Rightarrow y_3x_3+dy_3\geqslant a_2+a_3\\ y_3(x_3-\frac{a_3}{y_3}-\frac{a_2}{y_3}+d+d)\geqslant a_1\Rightarrow y_3x_3+2dy_3\geqslant a_1+a_2+a_3 \end{cases} \]很明显是三个式子。
于是考虑通项,第 \(i\) 关的总要求为:
其中 \(sum\) 是 \(a\) 的前缀和。
非常神仙地,把后面的东西拆开得到:
我们可以把它看做 \((x_i+di,sum_i)\) 到 \((dj,sum_{j-1})\) 连线的最大斜率,很明显前者是一个定点,后者的所有点我们都可以在枚举 \(i\) 之前得到并放进二维平面。
我们考虑维护一个 \(i\) 之前所有点的下凸壳。注意到 \(sum_{j-1}\) 和 \(dj\) 都递增,所以维护的下凸壳一定不包括斜率为负数的线在凸壳上,所以连向凸壳内的点一定劣于连到凸壳上的点(因为一定可以在 \(y\) 坐标不变的情况下向右取到凸壳上的点,一定可以减小斜率)。
然后每次对于一个定点,在凸壳上二分即可。用单调栈维护凸壳(所以你可以把单调队列维护斜率优化理解成,队尾的出口是一个单调栈维护凸壳,队首的出口是一个单调栈弹掉斜率在以后两端都不会再成为答案点的线)。
好题一道。
P2617 [SDOI2009] Bill的挑战
做一个 \(g_{i,ch}\) 表示所有字符串中第 \(i\) 个字符出现 \(ch\) 的集合,注意问号算作出现。
\(dp_{i,s}\) 表示前 \(i\) 个字符,匹配上的字符串集合为 \(s\) 的方案数,很容易得到枚举 \(i+1\) 位置字符的转移:
答案和初值都是一般的。
感觉很好理解,但是想不到。
to do list:还有容斥做法。
P2515 [HAOI2010] 软件安装
考虑建图,连接 \(D_i\to i\)。从一个 \(i\) 开始往回跳,要么跳到自己的祖先,要么构成环回到自己。所以可以考虑一个环里面必须全选,因为互相依赖。
继续考虑,环上每个点一定是没有不在环上的入点的,所以我们把环缩点之后就会成为一个树的根,下面一定接了一棵树(不可能接环在下面,否则就一定有点有不在环上的入点)。所以给所有强联通分量缩点,然后会变成一个森林,造一个节点 \(0\) 使它变成一棵树。
剩下的就是树上背包模板了。
\(dp_{i,j}\) 表示子树为 \(i\) 的节点总空间为 \(j\) 的最大价值。但这道题的合并方式与常规所想的那种复杂度为 \(O(n^2)\) 的树形背包不同,这个题就是个普通树形背包,我写的是 \(O(nm^2)\) 的,不过好像有 \(O(nm)\) 神奇做法。
考虑建图,然后想到环这一步有点难。其实多考虑是能考虑出来的。
CF311B Cats Transport
首先可以明显注意到,如果一只猫能被带走肯定立刻被带走。
所以考虑某只猫要恰好被接走某个人需要在什么时候出发。这个东西是很好算出来的,记为新的 \(t_i\)。
然后现在对 \(t\) 排序就消除了之前对 \(t\) 排序时距离的影响,问题转化成,一个人可以接到所有 \(t\) 小于等于其出发时间的猫,求最小等待时间和。很明显设出发时间为 \(T\),那么猫 \(i\) 的等待时间就是 \(\max(T-t_i,0)\)。
很明显出发时间需要满足可以恰好接到某只猫,否则严格劣。不妨考虑可以接到的猫里面 \(t\) 最大的那一只,于是设计 \(dp_{i,j}\) 表示第 \(i\) 个人可以接到的最后一只猫是 \(j\),考虑转移。
后面的和式非常难受,我们拆一下和式并且对 \(t\) 做前缀和记为 \(sum\)。于是有:
这样做是 \(O(pm^2)\) 的。
考虑斜率优化。第一维已经不重要了,可以通过类似 P4072 的方法在枚举 \(i\) 时维护 \(i-1\) 的下凸壳实现。考虑得到:
很明显 \(x\) 是单调递增的所以可以斜率优化,而 \(t_j\) 因为排好序的缘故是单调递增的,可以单调队列做。
第一步转化到新的 \(t\) 比较难,后面比较板和简单了。注意斜率优化钦定 \(k,x,y\) 的时候,\(k\) 是和枚举到的有关的,\(x,y\) 是和不想枚举、需要斜优掉的有关的。详见斜率优化 DP 学习笔记的中间“做对应”的那一部分。
T373059 格斗大赛(botbox)
题意:
有 \(n\) 个机器人类型为 \(A,B,C\) 三个中的一种,最初 \(1\) 号是擂主,\(2\sim n\) 号机器人顺序在队列中等待战斗。
每次你执行如下操作:
- 将队首机器人与擂主战斗。其中 \(A\) 型机器人可以打败 \(B\) 型,\(B\) 型可以打败 \(C\) 型,\(C\) 型可以打败 \(A\) 型。
- 如果队首机器人胜利,队首机器人成为擂主,原擂主进入队尾;反之则队首机器人进入队尾。
输出 \(k\) 次操作后,擂主机器人的类型和队列中机器人的类型。
乍一看不会做。乍一看榜 CQ E 队不会做。再一看 CQ 没人会做。
首先你就必须把擂主当成队首,这样你的队列的变化就很简单,如果擂主胜利,相当于 \(2\sim n\) 的部分左移一位,左移出去的在右边填充;如果失败就相当于 \(1\sim n\) 左移一位,左移出去的往右边填充。
那考虑把原序列分成若干块,每一块的开头都能打败块内后面的选手,我们取极长块,也就是说后一块的开头能打败前一块的开头,并且第一块的开头可以打败最后一块的开头,然后你就可以发现,因为你的一个块首先会把除了块头的东西都先甩到最后,然后再把块头甩到最后(因为一定会被下一块的块头打败)那么在 \(n\) 次操作后原先是同块的部分仍然是连续的。
但是分块情况呢?我不妨考虑,对于每个块,将最开始的块头的类型标为 \(1\),然后将块内所有与块头类型相同的都标为 \(1\),不同的标为 \(0\),很明显一个块只有两种类型的机器人。
然后你就会发现,你每一次一定是把一个 \(1\) 甩到了最后,因此你每一块必然是,前面有一段原块的后缀,然后后面有一大堆 \(1\)。假设你前面有一堆 \(0\),那么这堆 \(0\) 必然会自己分在一起,然后直到下一个 \(1\),这堆 \(0\) 就会一直存在,在这里面合并后面的 \(0\) 然后越来越多,轮到下一个 \(1\) 到队首的时候,这个新 \(1\) 后面的 \(0\) 就会到后面去,然后和前面已经有的 \(0\) 在一起,最后这个 \(1\) 甩到末尾,如此往复。
很明显一大串连续的相同类型机器人是不影响我们做原问题的,因为他们一定会被绑定在一起进行接下来的一轮又一轮(因为对连续相同类型机器人的操作一定是相同的,都一定会被放到最后去),也就是说,它们不会动。
于是你单看每一块,它每一轮都会把第一个 \(1\) 甩到最后,然后块头就会成若干个 \(0\),但是我们知道一堆连续的东西是不会动的,所以即便在新的一轮中这些连续的东西被分到了另一块,原块的块头被收缩到了下一个 \(1\) 的位置,操作过后前面那一堆连续的东西还会在前面,同理后面的一堆 \(1\) 也会一直在后面,中间夹住的原块的后缀也就一定没动,说明原块都是独立的(可能新的分块方法与原块不同,但是按照原块的分块方法可以发现,同一块内的元素没有变,只是把第一个 \(1\) 甩到最后了)。
于是你可以先计算出要甩多少轮,也就是每一块里面有多少个 \(1\) 要甩到最后,这是简单的。
不过你发现我们必须要做到第一块的开头可以打败最后一块的开头(不能平局,否则它们之间有先后关系会不独立),维护第一块的开头是很轻松的,最后一块的开头可以考虑比较它与新来的所有的尾部的关系。容易证明在 \(n\) 次战斗以内就可以找到这样一个状态。
然后你每 \(n\) 个一个周期,把每一块里面前周期数个 \(1\) 移动到最后。如果周期数比 \(1\) 多那就直接把所有 \(1\) 都扔到后面,因为很明显在达到这样的状态后这一块永远也不会动了(一堆 \(0\) 和一堆 \(1\) 都达到了稳定)。然后对于剩下的 \(k\bmod n\) 轮直接暴力就行了。
注意需要特殊处理一整个东西是一块的情况。
GYM104479J Joining Arrays
说白了就是,有一个字典序最大的排列满足存在一种方式,在 \(n\) 个集合当中每个选出一个,按照数值从小到大排名,得到的每个数的排名序列就是那个排列,特别的,相同的数越靠后的排名越靠后。计数对于字典序最大的那个排列有多少种选数方式。
首先搬题的时候搬题人做了转化,其实原题的字典序最大就是让每一个位置它后面的数中严格小于它的尽量多,也就是把尽量大的数往前放。不妨设 \(B_i=\sum\limits_{j=i+1}^n[a_j<a_i]\),然后我要最大化 \(B\) 的字典序。
然后你考虑这个东西怎么做,首先你可以先找出一个上界,也就是贪心地求一个序列 \(P\),使得它的 \(B\) 达到最大的字典序,并且每个集合中选的数都不能再大了。
想要求这个 \(P\) 非常简单,你可以做一个已选数的前缀 \(\min\),第一次肯定选择集合 \(1\) 中最大的数,然后你考虑集合 \(2\sim n\):
- 如果存在一个小于前缀 \(\min\) 的数,为了使前面所有的 \(B\) 尽量大,我一定取所有小于前缀 \(\min\) 的数中最大的。
- 如果不存在一个大于前缀 \(\min\) 的数,为了使前面所有的 \(B\) 尽量大,假设 \(k\) 为选择某个数会导致前面的已选数中有多少个大于等于它,那么我一定取到与集合最小数的 \(k\) 相等的所有数中最大的。很明显数越大,\(k\) 越小。
按照这样选出来的一个序列 \(P\) 一定满足,里面任意一个位置上选的数都不能是对应集合中原数的后继了,因为再大就会减少前面东西的 \(B\)。
于是你就会发现问题变成了你可以选择每个集合的从最小数开始的连续一段数,使得 \(B\) 不会改变,问方案总数。
考虑 dp。很容易可以想到先选后面的再选前面的。\(dp_{i,j}\) 表示从后往前选到第 \(i\) 个集合的第 \(j\) 个元素,考虑转移。
对于第 \(i\) 个集合,它一定只向它上面的最远的满足 \(P_j>P_i\),并且是所有 \(P_j>P_i\) 的集合中 \(P_j\) 最小的那个集合 \(j\) 转移,如果不存在就转移到虚点 \(0\)。转移的时候,对于第 \(i\) 集合的某个元素,它会对 \(j\) 集合中所有大于它的元素并且小于等于 \(j\) 集合上界 \(P_j\) 的元素做贡献,只有这样才能保证集合 \(i\) 对 \(j\) 集合所做的贡献最大,因为很明显不能选比上界大的数,也不能选小于它的数否则上界会导致集合 \(i\) 对集合 \(j\) 的贡献不够大。
这里向最远的转移是因为如果不这样做,就可能会导致两个集合 \(P_i=P_j\) 且 \(i<j\) 若 \(i\) 中有小于 \(P_j\) 的数 dp 值就会被初始化成 \(1\),然而很明显这里是不可能有贡献的。
然后你考虑转移之间是不是独立的。也就是说,如果两个集合都可以转移到同一个集合,这两者是否可以互相影响。然而这两个集合的可选数区间是不相交的。
考虑这两个集合为 \(S_j,S_k\),钦定 \(j\) 在 \(k\) 上方,要么 \(P_i>P_k\geqslant P_j\),那么 \(S_k\) 里面一定没有比 \(P_j\) 小的数(如果有,那么选那个数一定比现在的这个好,因为对 \(j\) 的贡献更好,上界就会变成那个数从而 \(<P_j\)),因此 \(j,k\) 可选数区间不相交;如果 \(P_i>P_j>P_k\),那么 \(k\) 集合就一定不会往 \(i\) 上转移。
于是现在我们知道,到每个集合每个元素的转移都是独立的,所以对于从集合到元素上的每一个转移,这些转移的方案需要乘起来,因为不同集合之间的方案是独立的,可以乘法原理。但每个转移上,其实是同一集合里可能有多个元素对同个元素做贡献,这些贡献需要加起来,因为来自同个集合,只能选择其中一种。
实现上这里的加乘关系很恶心。对于每次 \(S_i\to S_j\) 的转移,很明显 \(S_i\) 每个元素所能贡献到的 \(S_j\) 集合内的元素是连续的,并且在抛弃掉 \(>P_j\) 的数之后是一段后缀,于是你每次在一个中介数组上做后缀加(很容易想到用本质是差分,但弱于差分的方式实现),然后马上前缀和一遍中介数组并乘在原先 \(S_j\) 的 dp 数组上。
然而复杂度是错的,因为你发现转移 \(S_i\to S_j\) 的时候要扫 \(S_j\),用类似菊花图卡扫父亲连接到的点的方式,只要有一大堆转移连在一个特别大的集合上就寄了。
然而由于李教练的速度加成,LH 没有管这个事情过掉了原数据。
那么你考虑一下怎么不做一遍就前缀和,你可以发现,转移的时候 \(S_j\) 每个元素的 dp 值要乘上的东西是这样的:
于是你考虑做商分(本质就是差分),转移的时候给 \(dp_1\) 的左边界乘一个 \(dp_1\),给 \(dp_2\) 的左边界乘一个 \(\frac{dp_1+dp_2}{dp_1}\),以此类推,到了某个集合开始转移之前从左往右前缀乘一遍就好了。除法用逆元实现。
比较难写。算 \(P\) 的时候情况二如果强行实现支持插入、查排名的数据结构需要平衡树,所以你可以考虑如何判断“和最小数 \(k\) 相等”,因此直接把前面所有已选的数放入 set,在 set 中二分直到二分到的值发生变化即可。注意需要考虑二分不到值所以比较指针。
乱七八糟的 case 非常多,最后写了 5.4KB 终于过了。
[ABC091D] Two Sequences
有点反套路的题。不用 Trie 树也不用数位 dp。
考虑拆位算贡献。计算位 \(k\) 的贡献。这里有个实现细节是最后的异或和可能有 \(29\) 位,因为两个 \(<2^{28}\) 可以加在一起。
下文称位 \(k\) 为二进制下表示 \(2^{k-1}\) 的那一位。从低位往高位考虑,发现高位没有用于是考虑用取模 \(2^k\) 的方式把两个数组的高位搞掉,然后可以发现,如果要使和的第 \(k\) 位为 \(1\),和必须要满足:
- \(2^{k-1}\leqslant a_i+b_j\leqslant 2^k-1\) 或 \(3\times 2^{k-1}\leqslant a_i+b_j\leqslant 2^{k+1}-1\)。
很明显因为取模所以这两个数相加不会超过 \(2^{k+1}\)。
因为可以枚举位数,对于每一位将 \(a,b\) 中任意一个排序,枚举未排序数组中取哪个数,用二分算出范围内另一个排序数组取值的数量就行了。
CF1859D Andrey and Escape from Capygrad
会做法不会实现的题。
很容易发现对于每个传送门传送到 \(b_i\) 都是不劣的,并且从一个位置出发一定只会向前走直到无法向前走,这两个结论都容易证明。其实到这个地方就可以用各种大型数据结构来做了,但是太麻烦所以放弃了。
有一个神仙的实现,你会发现只有 \([l_i,b_i]\) 是有用的,因为 \((b_i,r_i]\) 往回走了。所以每个询问就相当于在一条线段上走,遇到重合的线段可以换线段,问最右边可以到哪。这就是经典水题 P9166 [省选联考 2023] 火车站,乱做就行了。我用了一种比较巧妙的倒着做的扫描线。
CF1856E1 PermuTree (easy version)
其实是道简单题,但是是好题。
首先很容易发现的结论是对于一个子树 \(u\),需要把一些它的儿子的子树中所有的 \(a\) 都满足 \(<a_u\),另一些 \(>a_u\),假设第一种子树的点集是 \(S\),第二种子树的点集是 \(T\),这个子树的答案贡献就是 \(|S|\times |T|\),很明显如果交换 \(S,T\) 任意元素,答案都会变差,所以这样是最好的。
显然这样做每个子树都能分配到连续的权值,相当于一个子问题。于是问题变为将 \(u\) 子树的所有儿子分成两部分,很明显可以 01 背包出集合 \(S\) 的所有可能总点数(因为每个子树的大小 \(siz\) 是固定的,必须全选或不选),然后枚举。枚举的部分很明显是 \(O(n^2)\) 的,考虑背包复杂度。
每次背包需要倒序枚举 \(siz_u\to siz_{son_u}\),等价于枚举除了这个子树中的点以外的所有点,很明显每个点最多被枚举到 \(n\) 次所以时间复杂度是 \(O(n^2)\)。
CF1854A Dual
个人认为非常牛逼的构造题。只想出来了 A1 的一部分。
考虑这个操作的形式和单调性,那么我们有一个想法,给全非负的数组做一遍前缀和就能做到单调不减,做后缀和就能单调不增;给全非正的数组做一遍前缀和就能做到单调不增,做后缀和就能单调不减。这两个累前缀和的操作都需要 \(n-1\) 次普通操作。也就是说,我们只要在 \(31\) 次之内让所有数同号,A1 就解决了。很显然,我们只要掏出绝对值最大的那个数然后让所有数都被这个数加一遍肯定就完事了,那么我们就在 \(19\times 2=38\) 步 以内干掉了 A1。
考虑可不可以在 \(12\) 步之内让所有数同号干掉 A2。我们发现,和绝对值最大的数 \(k\) 同号的数没必要加它。假设与 \(k\) 同号的数有 \(m\) 个,那么我们只需要 \(n-m\leqslant 12\) 就可以了,当 \(m\geqslant 8\) 时,我们直接暴力加就赢了!
考虑 \(m<8\) 我们该怎么办。我们只能考虑用反号的数去把这些数填成另一种符号。然后我们惊奇地发现,即便反号的数是 \(\pm 1\),让它自己加自己 \(5\) 次之后绝对值也大于 \(20\)!所以我们只需要让某个反号的数自增 \(5\) 次,再把和 \(k\) 同号的数(最多 \(7\) 个)全部加上这个这个数,把符号反过来就完事了!
非常精巧的构造。有 \(0\) 的时候有一点细节需要注意一下。
P2507 [SCOI2008] 配对
有趣的题。
一眼都能想到,假设没有“相同的数不能配对”这个规则,那么直接把两个数组排序之后顺着配对肯定是最优的,分类讨论可以发现是因为交换任意两个都会使得答案变大。
那如果有了相同的数怎么办呢?你可以发现一个事实就是它一定只会和左边那个位置交换或者和右边那个位置交换。很容易理解,往更小的数交换或者往更大的数交换肯定不优。我想到这里就写了个假的贪心得了 \(30\) 分不会了。
你发现你肯定不能直接这么做,因为有可能右边那个数想和左边换,你现在和左边换了到时候换这个地方就生成后效性了。
但是你发现这总是左边、中间、右边三个人的游戏,你可以直接一次把三个捆绑在一起,因为你所有的交换都限于三个东西,有第四个涉及的时候一定不够好(可以考虑分成两个两个的交换,这样肯定还是没有相同数的配对),所以直接做 dp,\(dp_i\) 表示前 \(i\) 个配对好的最小值,转移从 \(i-3,i-2,i-1\) 分别枚举三个一组、两个一组、一个一组的所有情况。
枚举情况的时候可以发现三个一组的有些交换情况可以看成是另外两种情况各一个拼在一起的,可以不用重复枚举,减少码长。
答案就是 \(dp_n\)。
P3380 [SHOI2012] 随机树
SA 说做出来可以拿图灵奖。
P4099 [HEOI2013] SAO
这题做出来怎么就不能拿图灵奖了。
一眼就能想到给所有要求左右连有向边,然后可以得到一个树形图,问这个树形图的拓扑序数量。很明显在图上不好做,不妨连无向边,对于 \(u\) 要比 \(v\) 先完成的,\(u\to v\) 连 \(0\) 权边,反之连 \(1\) 权边,这样就确实得到了一棵树。
考虑树形 dp。\(dp_{i,j}\) 表示以 \(i\) 为根的子树,\(i\) 的排名为 \(j\) 的方案数。考虑用树形背包的方式合并。
假设父亲为 \(u\),目前需要合并的儿子为 \(v\)。不妨设 \(u\) 的排名 \(p_1\to p_3\),\(v\) 的排名 \(p_2\to p_4\)。
- 假设 \(u\to v\) 为 \(0\) 权边,那么必须保证 \(p_3<p_4\)。原本在 \(u\) 前面的 \(p_1-1\) 个点永远都至少会在 \(u\) 前面;原本在 \(u\) 后面的 \(siz_u-p_1\) 个点也会永远都至少在 \(u\) 后面,并且因为 \(p_3<p_4\),所以原本在 \(v\) 后面的 \(siz_v-p_2\) 个点加上一个 \(v\) 点也会永远在 \(u\) 后面,那么 \(u\) 前面就至少有 \(p_1-1\) 个点,因此 \(p_1\leqslant p_3\),后面至少有 \(siz_{u}+siz_{v}-{p_1}-p_2+1\) 个点,所以 \(p_3\leqslant p_1+p_2-1\)。所以 \(p_1\leqslant p_3\leqslant p_1+p_2-1\)。
- 假设 \(u\to v\) 为 \(1\) 权边,那么必须保证 \(p_4<p_3\)。原本在 \(u\) 后面的 \(siz_u-p_1\) 个点永远都至少会在 \(u\) 后面;原本在 \(u\) 前面的 \(p_1-1\) 个点也会永远都至少在 \(u\) 前面,并且因为 \(p_4<p_3\),所以原本在 \(v\) 前面的 \(p_2-1\) 个点加上一个 \(v\) 点也会永远在 \(u\) 前面,那么 \(u\) 前面就有 \(p_2+p_1-1\) 个点,因此 \(p_1+p_2\leqslant p_3\),后面至少有 \(siz_u-p_1\) 个点,所以 \(p_3\leqslant siz_v+p_1\)。所以 \(p_1+p_2\leqslant p_3\leqslant siz_v+p_1\)。
于是我们确定了 \(p_3\) 的范围。考虑枚举 \(p_1,p_2,p_3\)。很明显转移时要乘组合数,因为两边的方案可以交叉排布。\(p_3\) 前面的 \(p_3-1\) 个点必然有 \(p_1-1\) 个点来自原顺序,后面的 \(siz_u+siz_v-p_3\) 个点必然有 \(siz_u-p_1\) 个点来自原顺序。很明显确定了原顺序的位置之后,新合入的儿子的顺序只能顺着放,于是交叉排布的方案数就是 \({{p_3-1}\choose{p_1-1}}{{siz_u+siz_v-p_3}\choose{siz_u-p_1}}\),再乘上 \(dp_{u,p_1}dp_{v,p_2}\),即可加在 \(dp_{u,p_3}\) 上转移。注意树形背包转移时需要 tmp 数组避免新的 dp 值覆盖旧的导致转移错误。
两种情况的转移方程是一样的,但是枚举的范围不同。
然而这样做复杂度是 \(O(n^3)\) 的。只能拿到 \(40\) 分。考虑交换循环顺序将 \(p_2\) 的枚举放到最里面,然后你会发现我们的组合数里面没有 \(p_2\),我们要乘上的总是 \(dp_{v}\) 中连续的一段,所以随便前缀和优化即可。注意到离开 \(u\) 节点之后所有的 \(dp_u\) 都不会被单独使用,因此直接在 \(dp_u\) 数组上覆盖地做前缀和即可。
P2466 [SDOI2008] Sue 的小球
区间 dp 加费用提前计算。可以通过已经取完彩蛋的区间一定是 \(x\) 坐标连续的一段看出区间 dp。
很经典的区间 dp trick 是设 \(dp_{l,r,0/1}\) 表示搞定区间 \([l,r]\),\(0\) 表示目前位于 \(l\),\(1\) 表示目前位于 \(r\),转移 \(dp_{l,r}\) 时从 \(dp_{l+1,r},dp_{l,r-1}\) 转移而来。然而这个题你发现不好转移,你需要同时知道到这个点的时间和最大答案,所以考虑费用提前计算,不要管不重要的时间了。
这里的费用提前计算是,设 \(dp_{l,r,0/1}\) 表示取完区间 \([l,r]\) 中所有彩蛋,目前在哪,所有彩蛋已经向下坠了多少。转移十分显然,答案为 \(\frac{(\sum y)-\min(dp_{1,n,0},dp_{1,n,1})}{1000}\)。
这个区间 dp trick 适用于 ABC273F Hammer 2 和 [NOIST2023] 圣诞树。
P2501 [HAOI2006] 数字序列
图灵奖题。
这篇题解已经足够好了。
[ABC252Ex] K-th beautiful Necklace
把每种颜色的珠子放在一起,得到 \(m\) 个集合。
首先一眼看出 meet in the middle,把这 \(m\) 个集合分成两半,DFS 出两个部分所有可能的异或和,然后问题转变成两个序列 \(a,b\),求 \(a_i\oplus b_j\) 的第 \(K\) 大。
这个问题有一个显然的 \(O(n\log ^2 n)\) 做法,二分第 \(K\) 大数 \(x\),转成检查有多少个数大于等于它,只需要把 \(a\) 序列每个数从高位到低位插进 01-Trie,然后枚举 \(b\) 序列的某一个数,假设 \(b_j\) 某一位为 \(f\),但 \(x\) 这一位为 \(1\) 那么就会加上字典树中这一位是 \(f\) 的子树大小(该子树内有多少个数而不是多少个节点),然后走进 \(f\) 取非的子树钦定这一位与 \(x\) 这一位一样。而如果 \(x\) 这一位为 \(0\),那么不能加任何贡献,直接走进 \(f\) 的子树钦定这一位与 \(x\) 这一位一样。(类似数位 dp)
然而经过早上模拟赛 1.5 小时的卡常我得出结论:过不了。
原因是 meet in the middle 之后两个序列的长度为 \(5\times 10^5\) 左右,很明显这种数据范围根本过不了 \(O(n\log ^2 n)\),考虑去掉这只该死的 \(\log\)。
你发现你每次二分太讨厌了,每个数都要从字典树的根开始走,能不能做整体运动?
考虑按位算答案。假设目前对于每个数 \(i\) 在 01-Trie 上都在点 \(pos_i\),并且所有数走到目前这个点,是按照这种规则走的:如果答案这一位是 \(1\),就向反方向走(例如 \(a_i\) 这一位上是 \(0\),我考虑在字典树上走 \(1\) 这条边),否则就向正确的方向走。这样的话一定保证了每个数 \(a_i\) 代表的 \(pos_i\),在走到这个点的时候,\(a_i\) 异或上 \(pos_i\) 一路走过来组成的二进制数(还没走到的位看做 \(0\))无法与目前的答案比较出大小(前面的位都完全一样)。
那么对于所有数,向它这一位的反方向走到的点的子树大小和就是异或值中这一位上是 \(1\) 的数的总数,同时也是如果答案这一位上是 \(0\) 会多出来的大于答案的数的个数。因为通过前面无法比较出这些多出来的异或值与答案的大小,在这一位才可以。
很明显因为我从高位往低位确定,所以我一定已知已经一定大于答案的数的个数(高位大,低位就不管用了),如果加上这一位上多出来的之后总数大于等于 \(K\),说明这个位置绝对不能是 \(0\),因此答案的这一位一定为 \(1\),反之这个位置只能是 \(0\)。最后按照我们的规则走就行了。
稍微有一点点难理解。
需要调整一下参数。我这里取的是第一部分集合的总大小大于等于 \(\frac{5n}{11}\) 的时候分割,当然在题解中有更好的做法:shuffle 所有集合的顺序 \(100\) 次,然后取第一部分序列长度(即第一部分所有集合大小乘积)小于等于 \(3\times 10^5\) 且最大时的分配方案,这样做可以发现两边是比较平衡的(最大的两部分序列长度乘积约为 \(1.3\times 10^{11}\))。
然后你把第一部分放进 01-Trie 的时候,如果用题解高明的方式,可以发现因为每个数的二进制表示最多只有 \(60\),所以你只需要 \(60\times 3\times 10^5=1.8\times 10^7\) 个节点,这是完全够开的,但如果用 \(\frac{5n}{11}\) 的分割方式,我的代码开了 \(8\times 10^7\) 个节点才能通过此题。
P4728 [HNOI2009] 双递增序列
还以为是一道简单题,实则也是图灵奖题。我写了一个玄乎的 \(O(n^3)\) dp,自己都不是很懂为什么就对了。尝试线段树大力转移失败了。
这篇题解已经足够好了。
[ABC293F] Zero or One
首先你考虑暴力分解,但是你发现我们不可能枚举 \(n\) 以内的所有进制。
但是你发现进制越大,\(n\) 转成这个进制的数的长度就越小,而且这个数只有 \(01\) 所以可以暴力一下数是啥,再二分出是几进制。
算是另一种根号分治吧。tricky。
[ARC151C] 01 Game
SG 函数普通题,但我不会 SG 函数。
这是好的题解。
SG 函数的基础知识已经写在博客中了。
[ARC112C] DFS Game
又一道神仙博弈,需要一个树形 DP。题解不错。

浙公网安备 33010602011771号