刷题记录表1号
2026.4.19
ARC150D
如果我们设 E(x) 表示抽到每个点的期望次数,ans显然等于 \(\sum E(x)\),我们发现抽到每个点的期望次数只可能与他的直接祖先有关,所以考虑简化问题为一条链上,设当前有一条链,链上抽到的黑点数量为k,那么抽到未被抽到的点的概率为 \(\frac{di-k}{di}\) , 因此抽到未被抽到的点的期望是 \(\frac{di}{di-k}\) ,所以当前这条链完全被抽完的期望就是 \(\sum _{k=0} ^{di-1} \frac{di}{di-k}\) ,由于期望的线性性,抽到全部点的期望就是所有点被抽到的期望之和,所以每个点的被抽期望为 \(\sum _{k=1} ^{di} \frac{1}{k}\),累加即可
[AGC006C] Rabbit Exercise
观察到每次转移过后 \(f[x]=1/2*(2*f[x-1]-f[x])+1/2*(2*f[x+1]-f[x])\)
将式子整理一下 \(f[x-1]+f[x+1]-f[x]\)
设差分数组 \(d[i]=f[x]-f[x-1]\),
转换后
\(d[i]\) 变为 \(f[x-1]+f[x+1]-f[x]-f[x-1]=f[x+1]-f[x]=d[i+1]\)
\(d[i+1]\) 变为 \(d[i+1]=f[x+1]-(f[x-1]+f[x+1]-f[x])=f[x]-f[x-1]=d[i]\)
即\(swap(d[i],d[i+1])\),于是用一个类似快速幂的事情维护差分数组即可
Moving Robots
我们知道空方格子的总期望数,其实就是每个空方格子的期望之和,而每个空方格子的贡献是1,因此答案即为每个格子成为空方格子的概率之和,此时成功将问题简化,我们考虑对这 \(8*8\) 的格子进行编号,设 \(p[i][j]\) 表示格子i的机器人到格子j的概率,那么转移的时候即为 \(P[i][l]+=p[i][j]/kk[j].size()\) ,因为每一步的概率计算只与上一步有关系,因此开两个数组即可,最后的ans即为 \(\sum _{i=0} ^{63} \prod _{j=0} ^{63} 1.0-p[j][i]\)
[ABC454E] LRUD Moving
首先考虑无解的情况,我们考虑对网格进行黑白染色,设 \((1,1)\) 为黑色格子,通过黑白染色后,易得出 \((N,N)\) 也是黑色格子,并且走的时候一定是黑白黑白走出来的,所以对于一个合法的路径,黑色格子一定比白色格子数量多1,那么现在考虑一个 \(n\) 为奇数的网格,他初始时黑色比白色格子数量多1,那么修改过后无论何时也不可能是合法的,对于n为偶数网格,也必须使 \(a+b\) 为奇数,即落在白色格子上。
此时情况必定为 \(Yes\) ,我们发现只需要对有障碍物的两行上下行走,其他不变,为简化分讨,我们用 \(up,down,left,right\) 来分别表示上下左右的无影响区域,他们的行走方式显然固定且重复,这样真正需要分讨的只剩下以障碍物为中心的一个2*2的格子,此题完成
2026.4.20
AT_acl1_e Shuffle Window
题目中的这个等概率随机打散听起来很不友好,详细展开,事实上可以将其抽象为一个袋子,最初袋子中有 \(k\) 个球,每次摸出一个后再放出一个,重复此操作,这样是不是更好理解?
由于本题是排列,不存在重复情况,我们思考,在原序列中若两个数是逆序对或不是逆序对,且两个数字并没有同时存在于袋子中,那么显然对答案没有改变,故在此不考虑。反之,只要两个数字同时存在于袋子中,就可能对答案有修改,所以我们首先处理出 \(f_i\)表示数字进入袋子的时间,那么两个球同时存在于袋子的概率为 \((\frac{k-1}{k})^{f_r-f_l}\) ,对于之前是逆序对的,显然不交换位置有贡献,反之没有,他对期望的贡献为 \(-\frac{1}{2}(\frac{k-1}{k})^{f_r-f_l}\);对于之前不是逆序对的则正好相反;因此设I表示原逆序对的数量,则答案为
化简这个式子即可,对于大于小于的情况分类,当然使用树状数组
[ABC454F] Make it Palindrome 2
想让序列变得回文,我们首先需要让两两对应的数差值相等,于是处理出 \(b_i\) 表示两两对应的数的差值,此时问题转变为将其变为全0串的最小步数。
此时还是区间修改,考虑将其变为差分数组 \(c_i\),这样将问题从区间修改转变为单点修改,最后记得多开一个,使其保证差分数组正数之和=负数之和,我们知道在 %m 的意义下,我们可以将一个数增大或减小两种方法将其减为0,所以我们将>=0和<0分别塞入两个数组里,由于最小的答案一定是正数之和,所以我们每次贪心地将整数中最大的-m变到负数中,再将最小的负数+m变为正数,这样尽量使正数之和变小,动态维护这个值即可。
【模板】线段树合并 / [Vani 有约会] 雨天的尾巴
首先本题每次要在 \(x-y\) 的路径上更新,很容易联想到树上差分,即在 \(x\) 和 \(y\) 位置上+1,在 \(lca(x,y)\) 和 \(fa[0][lca(x,y)]\) 位置上-1,然后就是线段树自下而上合并的板子,本题比较精妙的点是要求在相同数量下编号最小,于是我们可以用 maxx 的第二关键字存储编号的相反数,这样在线段树合并取max时,可以自动更新为编号最小的。
[Usaco2017 Jan]Promotion Counting
线段树合并的板子,唯一的区别是需要写一个ask函数来访问某棵数中 \(>k\) 的位置的个数,因为是 \(>k\) ,所以不能计算 \(l==r\) 的情况,其他的都是好做的
[POI 2011] ROT-Tree Rotations
本题中要求逆序对的个数,并且允许每一棵树内的两个叉树调换位置,求逆序对的最小值,由于每个子树在与其他子树合并时才会产生更多的贡献,所以要使用线段树合并,关键在于如何统计答案,我们考虑用归并排序的思想,子树合并时只可能两棵树左右调换位置,我们将两棵树同时二分,用每棵树左半部分的贡献乘上右半部分的贡献,即产生的逆序对的个数,在往下递归拿上每一部分两个数的贡献 (有点类似于CDQ分治) ,发现这样的话会产生两个答案,取 \(min\) 加到答案上即可
2026.4.21
【模板】线段树分裂
观察除第0个操作外,其他地方都是用线段树合并可做的,这里讲一下线段树合并,分裂时我们需要有一个端点值k,表示该棵树中前k小的点在x,后半部分则在y中,我们首先看该点的左子树的点的数量,设其为v:
当 \(k<v\) 时,那么右子树全部都归于新的线段树,直接交换 \(rc_x\) 和 \(rc_y\) 就能做到一键复制,然后递归到左子树中进行分裂;
当 \(k > v\) 的时候,左子树显然仍归于x,递归分裂右子树即可;
当 \(k==v\) 时,此时说明左子树归x,右子树归y,分好后直接回溯即可;
最后x的当前位置显然为 \(k\),y的当前位置显然为 \(cnt_x-k\),这样就分裂完成了,对于本题,我们可以先分裂出来 \([1,x-1]\) ,\([x,y]\),\([y+1,n]\) 三个部分,再将第一,三部分合并起来就能完成
「TJOI / HEOI2016」排序
本题我们虽不知道这个数字到底是多少,但我们可以判断这个数字相对于某一个数字是大还是小,我们考虑二分答案,每次将 \(<mid\) 的值在线段树上变为 \(0\),反之变为 \(1\) ,则我们将这一串值抽象成了 \(1\) 个 \(01\) 序列,我们对这个 \(01\) 序列排序时,复杂度仅为 \(O(log_2 n)\) 。
若排完序后,\(q\) 位置为 \(0\),说明其在实际的序列中的\(ans<x\),反之则证明排完序后 \(ans>=x\)
那我们相当于找到最大的 \(x\),使其排完序后位置为1即可
「FJOI2018」领导集团问题
先说一下序列 \(LIS\),设 \(f_i\) 表示 \(1\) 个长度为 \(i\) 的不下降子序列的末尾值最小是多少,那么对于一个新加入的值,我们在序列中二分出来第一个大于这个数的 \(f\) 并更新,如果这个数大于序列中的任意一个数,那么就新开一个 \(f\) ,表示最长的子序列可以加 \(1\) ,最后的答案就是 \(f\) 数组的长度。
那么本题就是一道树上 \(LIS\) 问题,我们设集合 \(f_u\) 表示在 \(u\) 的子树中 \(LIS\) 序列中的最小值最大是多少,对于叶子节点,他们初始显然为 \(w_u\) ,而在向上合并的时候对于相邻两棵子树内贡献显然不冲突,直接启发式合并加到父亲的集合里即可,对于父亲的点权,我们像LIS一样更新集合,如果他现在就是集合中间最小的那么直接让集合的长度 \(+1\) 即可,最后的答案就是根节点的集合 \(f\) 的长度
Dynamic Connectivity
这是一道可撤销并查集的模板题,问题就在于删边,如果删边,那么可能会导致原来连接起来的两个连通块分离开来,那么很容易就会想到把连接之前两个连通块的信息存储起来,但有问题就是删边的顺序和时间都不是固定的,可能后面连接起来的边会改变之前的联通情况。
于是乎,我们可以记录每条边存在的时间段,然后做一个类似于线段树的事情,每条边都在自己覆盖到的时间段内连边,这样当我们 \(dfs\) 向下查找的时候,如果当前时间段内这条边完全存在,我们就可以直接找到这条边对连通块的贡献,然后直接将本次的连接信息压入到一个栈里,此处我们不用担心回溯时后面边贡献会消失,因为后面的边在栈中会被先消除;连接完之后记录一下当前的连通块数量在删边回溯即可
这里有一个特别需要注意的情况,我们在删边的时候,只是将两个根节点的关系分开了,并没有处理两个连通块内部的情况,这意味着你在合并的时候不能使用路径压缩把儿子们都赋值到他的上面,如果害怕常数过大,这里可以使用启发式合并
2026.4.22
Lomsat gelral
我们对于每个线段树节点存储一个 \(sum\) 表示当前出现最多的颜色的次数是多少,\(ans\) 记录询问的答案,初始时先存储每个节点的信息,之后在线段树合并,如果有多个数量是最多的,那么就让 \(ans\) 等于两个子节点的的 \(ans\) 之和,否则就让其等于 \(sum\) 最大的 \(ans\)
[HNOI2012]永无乡
本题中的建边相当于线段树合并,并且没有删边操作,只需要 \(cnt\) 表示当前点所表示的范围内的岛屿的数量,每次合并时让两个岛屿的根节点做线段树合并,访问某棵线段树的第 \(k\) 小即可
[NOIP 2016 提高组] 天天爱跑步
对于每一条路径,考虑这条路径上的点 \(x\) 能否观测到这条线路上的玩家,但由于x所在地方不同,他们的计算方式也不同,这里我们分类讨论:
- 当 \(x\) 在 \(u \rightarrow lca\) 的时候 ,此时 \(u\) 到 \(x\) 的距离 (即 \(w_x\) ) 应为 \(w_x=dep_u-dep_x\),整理得 \(dep_u=dep_x+w_x\)
- 当 \(x\) 在 \(v \leftarrow lca\) 得时候,此时 \(u\) 到 \(x\) 的距离 (即 \(w_x\) ) 应为 \(w_x=dep_s-dep_{lca}+dep_x-dep_{lca}\),整理得 \(2*dep_{lca}-dep_s=dep_x-w_x\)
那么对于每个节点我们开两个线段树维护上述得差分数组,即 \(u,v\) 两点加 1,\(lca\) 和 $ f_{0,lca} $ 位置-1,处理完后自上而下进行线段树合并,合并完后统计每个节点的贡献即可
[COCI 2010/2011 #6] STEP
线段树码农题,维护 \(lans,rans,ans,fir,lst\) 表示从左端点开始得区间最大值,从右端点开始的区间最大值,该区间内最大的值,区间内的第一个元素值,区间内的最后一个元素值,剩下的转移即可
[PKUWC2018] Minimax (黑)
本蒟蒻为数不多的黑,首先我们既然想知道根节点每个权值的概率,那么必定需要自上而下的转移,设 \(f_{i,j}\) 表示第i个节点权值为 \(j\) 的概率,那么有转移式子:
简单解释一下这个式子,如果我们钦定该节点的 \(j\) 的权值由左边的儿子节点提供,那么右节点就会有两种情况:第一种,选择最大值,那么右节点必须是较小值,把小于他的概率之和乘上即可,如果选择最小值,概率为 \(1-p_i\) ,在把大于他的概率之和乘上即可,钦定右儿子提供亦如此,目前40分。
观察到这个式子是 \(O(n^2)\) 的,发现瓶颈在于维护这个前后缀,我们将公式简化,设
\(A[j] = p * (R中小于j的和) + (1-p) * (R中大于j的和)\)
\(B[j] = p * (L中小于j的和) + (1-p) * (L中大于j的和)\)
\(L[i]=f_{l,k},R[i]=f_{r,k}\)
然后我们设概率为类型建造n棵线段树即可
atcoder ndpc
A - Polyomino
我们设 \(dp_i\) 表示已经拼好了前 \(i\) 列方案数,显然,我们可以放置一个竖的木块使得加上一个 \(dp_{i-1}\) ,也可以放置两个横着的木块使其从 \(dp_{i-2}\) 转移过来,比较难处理的是拐弯的木块,这种木块一共有四种摆放方式,且可以通过两个L木块与其他长条木块拼凑的方式,使 \(dp_i\) 可以加上 \(dp_1——dp_{i-3}\)的方案数,由于左右两个L型木块可以同时交换形态出现两种不同的方案数,所以最后这一部分的方案数需乘 \(2\) 。
B - DAG
由于该图是简单有向图,所以我们很容易联想到拓扑排序,由于我们只关心 \(1——n\) 的方案数,所以初始时我们只需设 \(ans_1=1\),拓扑完之后输出 \(ans_n\) 即可
C - String
本题观察到 \(n\) 很大, 但只有3个判别串且长不超过10,此时我们有一个关于字符串DP的经典套路,设 \(dp_{i,j,k,l}\) 表示当前枚举到第 \(i\) 位,目前已经匹配到了 \(s_1\) 字符串的第 \(j\) 位,匹配到了 \(s_2\) 字符串的第 \(k\) 位,匹配到了 \(s_3\) 字符串的第 \(l\) 位的方案数,答案显然为所有没有和 \(s_1,s_2,s_3\) 匹配完全的方案数,初始时 \(dp_{0,0,0,0}=1\) ,转移时只需要枚举当前这一位的字符,和这3个字符串哪几位匹配成功,就加上它的方案数,时间复杂度为 \(O(n* \lvert s_1 \rvert * \lvert s_2 \rvert * \lvert s_3 \rvert *26)\)
D - Banknote
写假DP调了30分钟这一块
我最初的想法是先记录不找钱的答案为初始值,然后从低位到高位,每次钦定这一位归 \(0\) ,并向他的上一位进 \(1\),可以证明这样做现在的值一定大于原来的值,我们钦定当前这个值为付的钱数,用这个钱数和与其对应的零钱数的答案来更新 \(ans\) ,这样做很显然不对,举一个例子:当付款的金额为 \(91\) 时,最优的付钱数是 \(101\) ,这样就GG了。
好,开始说正解,我们设 \(dp_{i,0}\) 表示从低到高处理到前 \(i\) 位,当前这位是否向上一位进 \(1\) 所能拿到而最小花费,那么当前这一位有两种选择:
1.选择不进位,老老实实花费 \(op\) 的贡献
2.选择进位,上一位花费数量 \(+1\) ,当前这一位花费 \(10-op\)
注意,在最高位也能转移,所以答案即为 \(min(dp_{cnt,0},dp_{cnt,1}+1)\)
E - Summer Vacation
由于如果两个事件的时间有重叠,那么肯定两个任务不能同时选,因此我们需要维护每个任务后面与自己不重叠且影响最小的所有任务,这件事显然可以用倍增预处理,为了让每个任务相比下是最优秀的,我们考虑将任务按右端点排序,对于所有右端点相同的任务来说,只取左端点最大的,其他任务直接删去,设 \(st_{i,j}\) 表示当前第i个任务后面,相互两个不重叠的第 \(2^j\) 个任务编号是什么,在处理询问时,我们首先要找到最接近左端点的任务是什么,再从这个任务开始往后去倍增的跳来统计答案,搜索最靠近左端点的任务的时候可以采用二分右端点+暴力查找左端点来做,因为对于相同的右端点已经去重,所以复杂度应该是对的。
F - Set
本题要用到一个叫做笛卡尔树的东西,一句话概括,其本质是一个二叉树,对于一个节点的左儿子,一定是从该点对应的位置将该点对应的区间分成两半,左区间最小的值所在的位置就是他的左儿子,右儿子同理。在本题中,对应的就是如果在笛卡尔树中,左儿子与右儿子对应的子树都有被划分到集合里,那么这两个节点的 \(lca\) 也一定就在这个集合里,我们设 \(f_{i,j}\) 表示以 \(i\) 为根节点的子树中选择j个加入集合,能拿到的最小花费,转移方程分两步:
- 直接继承左儿子的值或直接继承右儿子的DP值
- 考虑枚举左子树的大小和右子树的大小,将俩个部分结合起来再加上当前节点的花费
dp[x][i+j+1]=min(dp[x][i+j+1],dp[ls[x]][i]+dp[rs[x]][j]+a[x]);
dp[x][i]=min(dp[x][i],dp[ls[x]][i]);
dp[x][j]=min(dp[x][j],dp[rs[x]][j]);
G - Mouth
首先我们需要知道一些结论:
- 不走回头路,即最优策略一定是从某一点出发沿一个方向走到另一端
- 对于每一个走到的节点,我们肯定是要使这个位置的 \(b_i=a_i\) 时是最优的
- 答案初始值为 \(\sum a_i\) ,如果走到0的位置,显然会使 \(a_i-b_i\) 多了1
所以我们将0的位置设为-1,显然此题变成了带修改的最大子段和,上线段树维护区间 \(maxx\) ,单点查询,区间修改即可
H - Coin
我们感性理解一下:每一个点对应的硬币的情况显然是一个区间,如果你能拿到最小值,那么你显然可以通过加入一定的硬币格子,使其达到硬币数更大的情况,最大值同理。
所以说我们对于每个点求出他最小和最大能拿到的区间数量,这个很好做,然后我们用一个差分数组将每个点的区间都放上去,统计答案即可。
K - Addition and Subtraction
这题我们需要了解一个重要的性质:就是在不考虑绝对值的情况下,如果我们能够拼出来整数 \(x\) ,那么一定能拼出来整数 \(-x\) (只需要我们把该加的数字变为减去,该减的数字变为加上就能实现取反,所以绝对值的情况可以通过负数转移到。
由于本题 \(\sum a_i \leq 2 \times 10^6\) ,我们考虑使用 \(bitset\) 来代表每个数字的可达性,这样我们可以通过左移或右移a位来表示是否可以达到这一个数 \(o = o | (o >> v) | (o << v)\)。
但直接更新复杂度爆炸,我们发现 \(n \leq 5 \times 10^6\),即 \(a_i\) 有大量重复的,我们开一个map统计后,直接采用类似多重背包二进制分组的方法,同一个值只塞入 log 次,复杂度正确
2026.5.11
AT_arc219_a A - Similarity
匹配字符串01位置,很容易想到01trie树,我们考虑将每一个01串的反串都压到trie树里,如果此时trie树变成满二叉树,说明无论任何一种走法,都会导致出现覆盖不到的字符串,那么肯定不合法,反之,我们记录一下每个结点的子树有多少个反串,走到trie树的缺口处即可
AT_arc219_b B - Reverse Permutation
我们考虑如果前i个数字与正确答案是相同的,那么一定存在将后面未匹配上的反转得到的答案,每次为n-i,然后我们考虑一种特殊情况,当全部的数字都和正确答案相同,那么一定存在一种已经排列好的情况,我们反转任意一个数字,此时序列没有变化,但贡献+1,这种情况特判即可
AT_arc219_c C - Traveling Door-to-Door Salesman (Elevator)
赛时就已经想出来了正解,但是没有码出来(码力太菜了)。既然上下移动是没有贡献的,那么我们可以将行数离散化,初始时的 \(ans\) 定义为每次都从左边出发,再从左边回来,拿到本行的贡献。然后我们发现每一行的贡献如果拆成左半部分和右半部分的话,答案是可能更优的,但是这样的话,我们就要选择一行渠去到对面的列和选择一行回来,这两行的贡献就变成了 \((m-1) \times 2\) 。这两行上面的点我们也就不用考虑。然后其他行的最优贡献可以枚举每一行的断点找到,但我们会发现即使这样,依然有效一些行的花费大于直接走过去的花费,所以我们每次再把贡献最大的两个行拿出来,钦定他们也组成一条来回的路线,这样不断取就是答案
AT_arc219_d D - Grid Game
本题更像是几中博弈方式的结合体,如果说我们不考虑石子数量,那么对于石子距离为 \((1,1)\) 的点,我们可以通过镜像博弈来使当前的堆变得无效,那么实际上只有奇数距离的堆才会对答案有贡献,我们再把距离抛开,只考虑堆的情况,因为每次只能拿k个石子,那有一种策略就是凑(k+1),即对面拿多少个,我们只需拿的和他之和是(k+1)即可,这样石子数量%(k+1)=0的堆也一定是无用的,那么剩下有用的堆只有距离 (1,1)为奇数,且石子数量不是(k+1)的倍数的石子堆,根据Nim博弈的经典结论,如果这些堆的石子数量异或和为0,后手获胜,否则先手获胜,此题完成
P2322 [HNOI2006] 最短母串问题
既然我们要找到一个最短的字符串使得该字符串拥有所有模式串作为子串,遇到若干个模式串和一个文本串,我们通常是要将所有的模式串插进AC自动机里,然后再做询问,我们钦定第 \(i\) 个字符串的结束节点的 \(ls\) 或上 \((1<<x)\),在建AC自动机的时候要把它的 \(fail\) 树上的贡献也拿过来。如果说我们在AC自动机上找到了一条路径使得,这条路径上的 \(ls\) 异或之和等于 \((1<<n)-1\),即这条路径组成的字符串包含了所有模式串作为子串,那么这种路径的字符串即为合法的,由于需要最小,我们可以 \(bfs\) ,并且让每个状态都记录自己的父亲状态,这样回溯时即可拿到字符串
G - Catch All Apples
本题中我们不妨先按照时间把每个点都排序一下,这样任意两个相邻的点之间如果可以使用同一个机器人,当且仅当 \(\lvert x_j -x_i\rvert \leq t_j-t_i\),我们把绝对值扔掉:
$ x_j-x_i \leq t_j-t_i \Rightarrow t_j-x_i \leq t_j-x_j $
$ x_j-x_i \geq t_i-t_j \Rightarrow t_i+x_i \leq t_j+x_j $
这样我们只需设 \(a_i=t_i+x_i,b_i=t_i-x_i\),然后每个机器人相当于做了一个最长上升子序列,而本题求的是需要的机器人的个数,根据 \(Dilworth\) 定理,实际上我们要求的是最大反偏序长度,经典二维偏序,用树状数组维护之即可
P3121 [USACO15FEB] Censoring G
首先我们需要将所有模式串插入AC自动机里,然后在文本串匹配的时候,我们需要开两个栈,第一个记录每一次跑到了AC自动机上的哪个节点,第二个记录我们需要输出的字符,当我们在AC自动机上跑到了一个模式串的末尾节点,我们为了将这个串删去,必定需要,让两个栈弹出 \(len\) 个信息,这样就会回到当时的AC自动机的节点,然后去继续匹配剩下的字符,到最后我们只需输出还存在栈里的节点

浙公网安备 33010602011771号