2025.8
UOJ750
妈妈这谁想得到啊。
题目等价于选两个不交的子集使得子集和相等。假如我们找到了任意两个和相等的子集 \(S,T\),分别去掉 \(S\cap T\) 后剩下的两个子集和一定也相等而且不交,所以只需要找任意两个和相等的子集。由于保证了 \(p<2^n-1\),所以鸽巢原理下一定有解。
扩展下上面鸽巢原理的想法,对于一个值域区间 \([l,r]\),如果和在 \([l,r]\) 内的子集个数大于 \(r-l+1\) 则说明一定存在和在 \([l,r]\) 内且相等的两个子集。这个性质允许我们直接对值域区间二分,具体来说先提前对序列折半搜索一下,然后每次二分 check 的时候对两半的子集提前排序然后跑个双指针数出来和在范围内的对数。
这样我们最后就能找到答案子集的和是多少了,容易再根据之前折半的信息找出来答案,复杂度 \(O(2^{n/2}\log p)\)。
UOJ783
感觉一点都不难啊/yiw/yiw。
为了方便,先把序列的初值看成题目里给的操作,先不管单点修改操作。
因为我们其实只关心序列的最值,所以对每个操作我们只需要关心它的 chkmax 操作是否执行了,则全局的最大值其实就是执行了 chkmax 的这些操作里的最大值。我们可以用更形式化一点的方式描述这一点,对于两个操作 \(i,j\),如果 \(t_i<t_j\land[l'_i,r'_i]\cap[l'_j,r'_j]\not=\emptyset\land y_i\ge x_j\),我们就连一条 \(i\to j\) 的有向边,这样会连出一个 DAG。求答案时,只要看序列的每个初值对应的操作在 DAG 上能走到的点的 \(y\) 的最大值即可。
发现这个限制就是类似三维偏序的东西,可以直接 CDQ 分治+线段树求出每个点能走到的最大值是多少。
单点修改操作的问题是不能够直接变成题目给的操作,但是发现这根本不重要,我们其实还是只关心这些位置新的值能触发的操作里的最大值。这其实还是个简单的偏序问题,跑完上面的 CDQ 分治后,拉出来这些单点修改操作再和之前的操作一起跑个双指针即可。
复杂度 \(O(n\log^2 n)\)。
P9168
再写一道这种题,加深一下理解,这里完全用 Hall 定理的角度来做这题。
发现题目本质上也是二分图带权匹配,令左部点为员工,右部点为树上的点,则一个员工可以和树上子树内的点匹配。
线段树分治把删除变成撤销,这样就只有加入员工的操作,考虑实时维护出来当前的最优匹配。
先上一手 Hall 定理,当前的局面合法等价于 \(\min_S(|N(S)|-|S|)\ge 0\)。简单地调整一下,注意对于任意一个员工,初始在他子树内的员工连向的右部点一定是他连向的右部点的子集,所以 \(N(S)\) 一定是一棵完整的子树。
换句话说,记 \(siz_u\) 表示 \(u\) 为根的子树大小,\(cnt_u\) 表示初始在 \(u\) 子树内的员工有几个匹配上了,当前的匹配合法当且仅当,对所有的 \(u\) 都有 \(siz_u-cnt_u\) 是非负的,我们记 \(w_u=siz_u-cnt_u\)。
加入一个初始在点 \(u\) 的员工时,如果他选择去匹配子树内某个点,那么 \(u\) 到根的 \(w\) 都会减 \(1\)。这时候我们检查一下 \(w\) 是否存在负数,如果不存在,说明我们有足够的位置给当前的新员工找到一个点去匹配,贪心地直接加入即可,把它的权值加入答案。
如果存在 \(w\) 变成了负数,那一定是在 \(u\) 的祖先处。此时我们需要撤销掉之前的一组匹配使得当前局面合法,找到 \(u\) 祖先里最深的点 \(f\) 满足 \(w_f<0\),则一定是选一个初始在 \(f\) 子树内的员工撤销,因为在其它祖先子树里撤销 \(w_f\) 不一定会改变(除非撤销的这个点就在 \(f\) 的子树里),而在 \(f\) 子树内撤销其它祖先就会直接合法。
在 dfs 序上开一棵线段树,对每个位置维护初始在这个点且已经匹配过的员工能力值最小是多少(因为初始一个点有多个员工,需要在叶子处开个 multiset
)。查询一下 \(f\) 子树里的最小值对应的员工,则一定会贪心地选择这个员工撤销。
当然我们还需要数据结构来支持 \(w\) 的修改,需要支持链加和查询链最小值,树剖/LCT/全局平衡二叉树即可。
使用树剖实现,带上线段树分治后复杂度 \(O(n\log^3 n)\)。
P11585
我为什么要来做这题。
\(k=1\)
对序列建立小根笛卡尔树,则点 \(u\) 为根的子树就代表以 \(h_u\) 为矩形高度向两侧扩展出的极长区间。
显然我们会选择一棵完整的笛卡尔树子树,直接枚举计算即可。
时间复杂度 \(O(n)\)。
\(k=2\)
此时可以把选择的情况分为两种:两个矩形行不交(左右结构),两个矩形列不交(上下结构)。
下文中记 \(len_u\) 表示笛卡尔树上 \(u\) 为根的子树的大小,也就是它对应的笛卡尔树区间的长度。
左右结构
我们可以认为两个矩形边界不会相邻,若相邻可以换成下一种上下结构的情况而答案不劣。
因为矩形边界不相邻,所以两个矩形在行上的区间一定还是一个完整的笛卡尔树区间。
预处理 \(pre_i,suf_i\) 表示前缀 \([1,i]\) 或者后缀 \([i,n]\) 里,最大的完整笛卡尔树子树矩形的面积是多少,枚举一条分界线 \(i\),用 \(pre_i+suf_i\) 去更新答案即可,这部分复杂度是 \(O(n)\) 的。
上下结构
考虑上下两个矩形在行上的区间,一定有上面矩形的区间是下面矩形区间的子区间,否则一定能调整。
下面矩形的区间肯定也是一个完整的笛卡尔树区间,假如选的是点 \(u\),那上面的矩形就会对应 \(u\) 子树内的一个点 \(v\),贡献为:
观察一下,问题相当于 \(u\) 子树内的每个点 \(v\) 对应了一条斜率为 \(-len_v\),截距为 \(len_v\times h_v\) 的直线,我们要求 \(x=h_u\) 时的最大点值。在笛卡尔树上每个点开一棵李超树维护子树内的直线,使用李超树合并的技巧维护即可,复杂度 \(O(n\log n)\)。
\(k=3\)
这时候情况就比较多了。
具体来说可以分为以下情况:三个矩形在行上不交(左中右结构),口凸/凸口结构,三个矩形在列上不交(上中下结构),一个矩形上方选两个行不交的矩形(凹形结构)。相信读者应该能理解这些结构对应什么情形,我们还是逐个解决。
左中右结构
类似 \(k=2\) 时左右结构的调整,我们仍然可以认为三个矩形的边界不会相邻,否则又可以调整成后面的情况计算。
此时的问题又相当于选出三个不交的笛卡尔树区间,这时候我们考虑直接 DP,设 \(f_{i,j}\) 表示前缀 \([1,i]\) 选了 \(j\) 个矩形的最大面积和。
转移是容易的,对于一个完整的笛卡尔树区间 \([l,r]\),加入一个 \(f_{l-1,j-1}\) 到 \(f_{r,j}\) 的转移即可,复杂度 \(O(n)\)。
口凸/凸口结构
这个的意思是,左边单独选了一个矩形,右边选了 \(k=2\) 时的上下结构的图形,或者反过来。
我们当然还可以认为左右两个图形不会相邻。在跑 \(k=2\) 的上下结构的算法时,把以 \(u\) 点作为下方矩形时的答案记为 \(val_u\)。现在枚举那个凸性结构下方的矩形选在 \(u\) 点,假设 \(u\) 对应的笛卡尔树区间为 \([l_u,r_u]\),用 \(val_u+\max(pre_{l_u-1},suf_{r_u+1})\) 更新答案即可。
算上求 \(val_u\) 的复杂度是 \(O(n\log n)\) 的。
上中下结构
考虑类似 \(k=2\) 的上下结构去做。枚举最下面矩形选在点 \(u\),中间的矩形选在子树内的点 \(v\),贡献写出来:
发现和上面形式其实完全一样,就是子树内的点 \(v\) 代表一条斜率为 \(-len_v\),截距为 \(val_v\) 的直线,求 \(x=h_u\) 时的最大点值。
同样李超树合并即可,复杂度 \(O(n\log n)\)。
凹形结构
欢迎来到最唐的情况。
枚举最底下的矩形在点 \(i\),则上面的两个矩形 \(j,k\) 会对应 \(i\) 子树内不成祖孙关系的两个点,贡献为:
发现想直接枚举 \(i\) 维护子树内的 \(j,k\) 是困难的,所以还是考虑进行一些调整。尝试放宽一下限制,如果 \(i\) 不是 \(j,k\) 的公共祖先,上面的式子并不会把答案算大。所以我们把限制改成选择两个不为祖孙关系的点 \(j,k\),\(i\) 则不做限制地任意选择。
如果对 \(j,k\) 也不再做限制呢?注意这时候答案可能就会算大了,但是我们还是先思考下有什么做法。令 \(C_i=\max_{len_j+len_k=i}(len_j\times h_j+len_k\times h_k)\),现在的问题形如对每个 \(i\) 求最大的 \(j\) 使得 \(C_j-h_i\times j\) 最大。到这里就变成了一个经典的模型,把 \((i,C_i)\) 看成平面上的若干个点,问题就是用斜率为 \(h_i\) 的直线去切点使得截距最大,经典结论是所有的 \((i,C_i)\) 只有上凸壳上的点是有用的。
那此时我们把每个点 \(i\) 看成平面上的一个点 \((len_i,len_i\times h_i)\),对这些点集求一个上凸壳,然后用这个上凸壳和自己做一遍闵可夫斯基和就能得到所有 \(C_i\) 了,这样不限制 \(j,k\) 不交我们能做到 \(O(n)\)。
接下来着手解决 \(j,k\) 不交的限制。对序列建线段树,线段树上的每个点开两个 vector
\(L,R\) 来存储点集。对一个点 \(u\),记它对应的笛卡尔树区间为 \([l_u,r_u]\),再记 \(P_u=(len_u,len_u\times h_u)\):
- 找出线段树上 \(l_u\) 对应叶子到根上的所有点,对是其父亲右儿子的点,在其父亲的 \(R\) 中加入 \(P_u\)。
- 找出线段树上 \(r_u\) 对应叶子到根上的所有点,对是其父亲左儿子的点,在其父亲的 \(L\) 中加入 \(P_u\)。
接下来,对线段树上每个点对应的 \(L,R\) 点集求个上凸壳。再维护一个全局点集 \(U\),将线段树上每个点对应的 \(L,R\) 做闵可夫斯基和后得到的所有点加入 \(U\) 中。最后再对 \(U\) 求上凸壳,枚举每个点在上面查询即可。
如何说明这个做法的正确性?首先对于线段树上任意一个点对应的 \(L,R\),显然分别从 \(L,R\) 中取出的点来自的笛卡尔树子树肯定是不交的。而对于任意两个不交的笛卡尔树区间 \([l_i,r_i],[l_j,r_j]\),不妨假设 \(r_i<l_j\),考虑叶子 \(r_i,l_j\) 在线段树上的 \(\text{LCA}\),显然叶子 \(r_i\) 在这个点左子树内,且叶子 \(l_j\) 在这个点右子树内,那 \(P_i+P_j\) 就会在线段树上的这个点被统计到,这说明我们不会统计漏。
那复杂度分析呢?如果精细实现是可以做到 \(O(n\log n)\) 的,重要的细节大概有:
- 将点插入线段树时提前排序好再插入,避免在线段树上排序。
- 对凸壳做闵可夫斯基和时,必须要实现成线性的归并。
- 不要直接对闵和出的所有点加入 \(U\) 排序,先记录每个横坐标对应的最大纵坐标即可。
综上我们以 \(O(n\log n)\) 的时间复杂度解决了本题,附一个 QOJ 上的提交记录,看上去常数还挺小。
ARC158F
非常巧妙的神题!而且感觉很小清新。
首先,对于操作序列里的相邻两个数,假设选择的数位分别为 \(i,j\),效果其实相当于把序列里的所有数按照第 \(j\) 位为第一关键字,第 \(i\) 位为第二关键字进行排序。如果出现了 \(i,j,i\) 这种结构,则最前面的那个 \(i\) 是完全没有用的。如此调整,我们可以发现对每个数位的操作都只需要保留最后一次就好了,这样一个操作序列可以直接用一个长度不超过 \(k\) 的排列描述。
其次,因为稳定排序不会改变相同数字之间的相对顺序,我们可以求出来 \(p_i\) 表示 \(b_i\) 对应了原来 \(a\) 序列里第几个数。
初步的分析已经差不多了,考虑计数。首先对于一个长度为 \(l\) 的排列,其恰好对应了 \(m\brace l\) 个完整的操作序列。但是这里 \(m\) 好像有点大,我们考虑容斥计算斯特林数,具体来说计算用 \(l\) 种颜色给长度为 \(m\) 的序列染色的方案数,再除以 \(l!\) 即可,而这个方案数可以钦定有几种颜色没出现过容斥:
当然我们还要求一个排列必须是合法的,接下来寻找下什么样的排列合法。题目要求最后的 \(b\) 序列有序,我们考虑拆分限制,变成要求每个 \(b_i\) 在 \(b_{i+1}\) 的前面。记 \(b_{i,j}\) 表示 \(b_i\) 的第 \(j\) 位数,\(S=\{j|b_{i,j}<b_{i+1,j}\},T=\{j|b_{i,j}>b_{i+1,j}\}\),则 \(b_i\) 带来的限制有:
- \(\forall x\in T\),\(x\) 在排列里最靠后的出现位置后面必须有一个 \(S\) 内的数。
- 如果 \(p_i>p_{i+1}\),排列里必须有 \(S\) 内的数。
到这里就能 DP 了,从后往前考虑,设 \(f_S\) 表示当前排列里的数集是 \(S\) 的方案数,枚举前面新加入的数 \(x\),我们在转移时直接处理第一个限制,也就是对所有 \(x\in T\) 的 \((S,T)\),我们都要求 \(T\not\subseteq U-S\)。我们可以提前对每个 \(x\) 处理出来每个 \(msk\) 是否存在 \(T\) 是 \(msk\) 的子集,直接高维前缀和复杂度 \(O(2^kk^2)\),这样就能完成 DP 转移了。
统计答案时解决第二个限制,和上面是类似的,也是高维前缀和预处理下就能判断了,对合法的 \(S\) 将答案累加上 \(f_S\times{m\brace |S|}\) 即可。
P7214
神了啊,感觉自己又是什么都没有想到/ll/ll。
以时间和序列为轴建系,每个治疗计划画在平面上是一个等腰三角形。
发现最后选的计划里肯定有 \(l=1\) 和 \(r=n\) 的,不然两侧的人是肯定治不好的。
那我们在这些等腰三角形之间连边,问题就是从一个 \(l=1\) 的等腰三角形出发,找一个 \(r=n\) 的等腰三角形为终点,求最短路。
什么样的两个等腰三角形会有边?画下就能发现其实就是 \(|t_i-t_j|\le r_j-l_i+1\) 时 \(i,j\) 之间有边,否则到时间了会接不上。
这个连边形式求最短路,一看就很好优化!按照 \(t\) 排序再建线段树,这样能拆掉绝对值。点权最短路有个性质是每个点的最短路一定在第一次松弛的时候就取到,所以跑 dijkstra 取出一个点的时候,直接在线段树上暴力找能松弛的点更新即可。
复杂度就是 \(O(n\log n)\)。
P7215
先把题意转化成求一个颜色数最少的连通块,使得连通块外不能有点的颜色和连通块内某点相同。
统计连通块的问题,点分治是常用的手法。每次算包含当前重心的答案,但是如果包含当前重心的最小连通块要跨过当前点分治的区域,我们直接丢掉不算了,因为这时候连通块肯定会包含之前的分治重心,以之前的重心为根时肯定统计过不比这个劣的答案了。
这样就保证了复杂度,\(O(n\log n)\)。
ARC176D
我草这不就是 NFLS 倒数第二场 NOI 模拟赛的 T3 吗。
怎么做的时候好像又忘了,发怒。
8.5 C
非常好题目,做爽了。
Part 1. 确定 \(A\) 和 \(K\) 时的做法
我们先假设 \(A\) 序列是确定的,考虑如何设计出来求一个 \(f(A,B,K)\) 的算法。
假设选好了要为哪 \(K\) 个人服务,应该按照什么样的顺序去服务?结论是会按照 \(B_i\) 从大到小的顺序。证明考虑邻项交换,对于两个人 \(i,j\) 且 \(B_i<B_j\),先服务 \(i\) 的答案是 \(\max(A_i+B_i,A_i+A_j+B_j)\),而先服务 \(j\) 的答案是 \(\max(A_j+B_j,A_j+A_i+B_i)\),显然后者不会比前者大。
假设确定为 \(p_1,p_2,\ldots,p_k\) 服务(这里假设已经是按照 \(B_i\) 从大到小排序好的顺序),则答案显然为:
这个式子其实可以写成:
所以初始令 \(ans=0\),从 \(i=K\) 开始倒着扫,每次做 \(ans\leftarrow A_{p_i}+\max(ans,B_{p_i})\) 即可。
以上的讨论都是基于 \(K\) 个人确定的情况,现在我们可以尝试设计一个 DP 来选择这 \(K\) 个人到底是哪些了。注意到上面求答案的过程是倒着扫的,所以现在把所有的 \(B_i\) 从小到大排序。设 \(f(i,j)\) 表示前 \(i\) 个数里选了 \(j\) 个的最小答案,转移就是:
这个 DP 直接做是 \(O(N^2)\) 的,而且后面计数的时候好像不好直接把它变成 DP 套 DP,所以我们需要对它再尝试优化下。
发现比较烦人的是后面的 \(\max\) 那项,尝试观察一下。发现对于 \(f(i-1,j)\le B_i\) 的 \(j\),转移后仍然满足 \(f(i,j)\le B_i\),且显然这种 \(j\) 是一段前缀。更进一步发现这些 \(j\) 处的值在以后的转移中再也不会被更新,可以直接提前计入答案!所以我们把这段前缀的值直接删去,就能砍掉转移式里的 \(\max\) 了。
重新看下现在的式子:
这个形式可太熟悉了,一脸 slope trick 的感觉(现在不妨认为 \(f(i-1,0)=B_i\))!容易归纳证明这个 DP 数组现在每一维都是下凸的了,即差分数组是单调的,所以直接开个小根堆维护差分数组,上面的式子相当于扔一个 \(a_i\) 进去。
当然我们还要支持前面的删去值不超过 \(B_i\) 的前缀那个操作,所以现在的完整流程大概是这样的:
-
初始时开一个小根堆 \(Q\),\(Q\) 内初始时只有一个元素 \(\infty\)(因为除了 \(f(0,0)\) 初值就应该是 \(\infty\)),并维护答案 \(ans\),初始为 \(0\)。
-
扫描 \(i=1,2,\ldots,N\),扫到一个 \(i\) 时,顺次执行以下操作:
- 当 \(ans\) 加上堆顶元素不超过 \(B_i\) 时,不断弹出堆顶元素并令 \(ans\) 加上它。
- 将堆顶的元素减少 \(B_i-ans\),再将 \(ans\) 赋值成 \(B_i\)(也就是将差分数组第 \(0\) 项变成 \(B_i\))。
为了方便,可以再添加一个 \(B_{N+1}=\infty\) 并扫到 \(N+1\)。
答案是什么?根据前面的流程,我们每弹出一次堆顶(也就是在前缀删一个数)就能确定一个 \(j\) 的取值,所以当我们总共弹了恰好 \(K\) 次堆顶时,\(ans\) 就是最后的答案。
因此当 \(A,B,K\) 都确定时,我们有一个 \(O(N\log N)\) 求出 \(f(A,B,K)\) 的算法。
值得一题的是直接从反悔贪心的角度好像也能得到上面的这一流程,限于本人水平暂时不展开篇幅。
Part 2. DP 计数
下一步就是对上面的 slope trick 的过程设计 DP 计数,类似题目有 AGC043E。
为了方便,我们规定当小根堆内有相同的数时,先加入堆的数更小,这样显然不会影响答案。
考虑直接用 DP 去维护上面的小根堆。看上去上面的过程其实很复杂,由于不可能做到记录堆里面目前有啥,所以好像也无法得知每次弹出的数具体是什么。但是我们可以直接钦定每个 \(A_i\) 最后有没有被弹出,如果钦定了一个 \(A_i\) 被弹出限制就是以后压入比 \(A_i\) 更小的数时也一定会一起弹出,钦定不被弹出同理。所以可以这么设计状态:令 \(dp(i,j,L,R,ans)\) 表示当前考虑了前 \(i-1\) 个数,钦定了 \(j\) 个数最后被弹出,以后加入 \(<L\) 的数必须被弹出,\(\ge R\) 的数不能被弹出,目前答案是 \(ans\) 的方案数。
转移个人觉得其实是容易的,但还是稍微展开讲一下。
首先考虑执行上面的弹出堆顶的操作,这里需要简单分类讨论下:
- 如果 \(ans\le b_i\),则我们之前钦定要被弹出的数在这里会全部被弹走,现在堆顶会剩下钦定没被弹的最小的数,它会减去 \(B_i-ans\)。所以此时执行 \(l\leftarrow 0,r\leftarrow r-B_i+ans,ans\leftarrow B_i\)。
- 否则说明之前钦定要被弹出的数在这里弹不完,要留到以后再弹,堆顶会有一个钦定被弹的数变成 \(ans-B_i\)。所以此时执行 \(l\leftarrow\min(l,ans-B_i)\) 即可。
现在得到了弹出堆顶后新的 \(l,r,ans\),枚举当前 \(a_i\) 的值再更新变量转移即可。
直接实现上面的 DP 过程复杂度是 \(O(N^4V^3)\) 的,看上去很爆但其实常数很小,简单卡常即可通过。不过再简单前缀和优化一下就能做到 \(O(N^4V^2)\) 了。
事实上本题可以做到 \(O(N^3V^2)\),水平所限不再展开篇幅。
8.5 D
\(tp=1\),可以发现答案是奇度点个数除以 \(2\),也可以直接从下往上贪心。
\(tp=2\),就是赛道修建。二分答案 \(mid\),维护每个点向父亲伸出的链最短是多少,同样每个点给自己儿子贪心匹配即可。
\(tp=3\),这才是本题需要解决的部分,我们先记第二问的答案是 \(lim\)。
可以设 \(dp_{i,j}\) 表示 \(i\) 向父亲伸出去的链长度为 \(j\) 的方案数,统计答案就按照第二问的贪心方式去枚举即可,但是在此之前要先给儿子之间配对。考虑先预处理出来 \(coef_{i,j}\) 表示第 \(i\) 个儿子和第 \(j\) 个儿子之间进行匹配的方案数,然后设 \(f_S\) 表示 \(S\) 集合内的儿子匹配的方案数。
这里直接状压看上去复杂度很高,但是我们稍微剪枝一下,首先转移可以直接枚举 lowbit 和谁进行了匹配,这样只会用到 popcount 是偶数的状态。其次查询 \(f_S\) 可以直接使用记搜实现,这样用不到的状态也不会碰。
直接实现就能通过 \(n\le 1000\) 了。继续优化的方向也很明显,复杂度瓶颈其实在合并 \(dp_{i,j}\) 上,注意 \(j\) 这一维不超过子树最大链长,直接长剖优化下即可。一个方便的实现方法是每个点开动态开点线段树,这样继承长儿子直接根接过来然后打全局乘的标记即可,而且线段树上的下标可以直接开成深度,这样就不需要平移之类的操作。
P11106
好难啊,,,
三方状态的 DP 就不说了。考虑两个子序列值域相交的时刻,后面只需要分别求个 LIS 和 LDS 就能计算答案了。
假如我们枚举是在 \(a_i\) 相交的,那前面的数应该是 \(<a_i\) 的都给第一个,\(>a_i\) 的都给第二个。先跑个扫描线,维护 \(w_{i,x}\) 表示考虑了前缀 \([1,i)\),如果把 \(<x\) 的数放在第一个序列,\(>x\) 的数放在第二个序列,答案是多少,我们最后只需要用到所有 \(w_{i,a_i}\)。然后再重新倒着枚举下相交位置 \(i\),使用数据结构求下后缀 LIS 和 LDS 即可。
复杂度 \(O(n\log n)\)。
P7213
好像又做不明白了,怎么回事呢。
先假设同种高度的石柱有区分,只需要最后给答案除以 \(2^n\)。倒着考虑,前面没倒闭的石柱最后的高度会形成若干连续段,如果当前的石柱高度在 \(1\) 所在的连续段内就直接倒闭了。直接 DP,设 \(f_{i,j}\) 表示倒着考虑到 \(i\),后面 \(1\sim j\) 高度的石柱都已出现的方案数(只关心 \(1\) 所在的那个连续段,其它部分先不管)。
这个状态为什么能转移?如果 \(i\) 钦定倒闭了直接从 \(f_{i+1,j}\) 转移,否则我们考虑 \(i\) 最后的高度是否为 \(j+1\),不是的话直接延迟钦定,先转移掉。如果是的话,考虑前面若干个延迟钦定的石柱,我们可以选几个过来和当前的石柱一起给连续段接上。
算系数的时候,需要再处理出来一个 \(g_i\),表示放 \(i\) 根石柱并让它们最后高度震成 \(1\sim i\) 这么一个子问题的方案,计算方法和上面最后一种情况是类似的。系数不展开写了,总之最后的复杂度是 \(O(n^3)\)。
P13275
原来 NOID2T2 这么可做的?但是前提是要容斥对东西!!一定要对权值容斥而不是集合不交!
具体来说,钦定 \(f(P)\subseteq X,f(Q)\subseteq Y\),我们设这样选出来一对 \((P,Q)\) 的方案数是 \(g(X,Y)\),写出来答案的式子:
好了,现在我们知道一个 \(g(X,Y)\) 对答案的贡献系数是 \((-1)^{|X|+|Y|}2^{|X\cap Y|}\) 了,接下来先考虑 \(g(X,Y)\) 怎么算。
注意需要满足 \(P,Q\) 不交的限制,这里我们可以简单编个组合意义,每个数要么选 \(P\) 要么选 \(Q\),不能都选,所以:
设 \(F_x=\prod_{x\subseteq i}(a_i+1),G_x=\prod_{x\subseteq i}\frac{2a_i+1}{(a_i+1)^2}\),两者容易高维后缀积 \(O(n2^n)\) 计算出来。
那现在已经容易计算一个 \(g(X,Y)\) 了,但是枚举 \(X,Y\) 太慢了,考虑继续加速。
问题无非在于每对 \(X,Y\) 对应的容斥系数不一样,我们考虑直接把系数拆进去。注意到 \(|X\cap Y|=|X|+|Y|-|X\cup Y|\),所以可以把系数改写成 \((-2)^{|X|}(-2)^{|Y|}2^{-|X\cup Y|}\)。到这里就非常好做了,令 \(F_x'=F_x(-2)^{|x|},G_x'=G_x2^{-|X|}\),答案就是 \(\sum_{x\cup y=z}F_x'F_y'G_z'\),让 \(F'\) 和自己做一遍或卷积再和 \(G'\) 点乘就好了,复杂度 \(O(n2^n)\)。
唉不对怎么没过啊?哦 \(a_i+1\) 可以等于 \(0\),逆元爆了,那咋办。
考虑维护一个关于 \(0\) 的多项式,我们发现两个关于 \(0\) 的多项式相除的时候只有它们最低次项次数一样的时候才有用,其实也就是两个多项式 \(F(x),G(x)\),\(\lim_{x\to0}\frac{F(x)}{G(x)}\) 的结果在 \(F(X),G(x)\) 最低次项次数一样时是对应的系数之比,否则就是 \(0\)(注意到本题不会出现 \(F\) 最低次更小使得极限无意义的情况)。那问题就简单了,把每个数表示成 \(a\times 0^b\),表示这个关于 \(0\) 的多项式的最低次项,加法的时候舍掉 \(b\) 更大的,乘法的时候 \(a\) 相乘 \(b\) 相加就好了。
8.7 C
有一说一我觉得是唐氏题。
对每个 \(i\) 维护 \(pos_i\) 表示最大的 \(j<i\) 使得 \([j,i]\) 没有重复颜色,显然 \(pos_i\) 单调不降。
那现在我们已经能解决不带修的问题了,询问区间内一段前缀会满足 \(pos_i<l\),这部分查询区间内 \(sum_i\) 的最大值即可。对于 \(pos_i\ge l\) 的后缀,则查询下区间内 \(sum_i-sum_{pos_i-1}\) 的最大值即可。具体的分界线可以二分得到。
那带修怎么做呢?记 \(pre_i\) 表示 \(i\) 前面第一个同色的位置,你发现一个 \(i\) 带来的限制就是,对于 \(j\ge i\) 必须有 \(pos_j>pre_i\),也就是做一件 chkmax 的事情。如果带上修改,我们不好解决之前一次 chkmax 的操作删除后的影响,所以我们直接线段树分治掉,变成加入一些限制即可。注意到因为单调性区间 chkmax 可以变成区间覆盖,所以直接用一个线段树维护这些限制是容易的,需要一个栈记录线段树上的信息修改来帮助撤销。时间复杂度 \(O(n\log^2 n)\),空间复杂度 \(O(n\log n)\)。
这题疑似还有个单侧递归做法,有空再看。
8.7 D
【2025 山东省二轮省队集训 D5T3】 卡牌游戏,再贴下我当时写的题解。
暴力的 dp 就是直接设 \(f_i\) 表示最后一个选的数是 \(a_i\) 的最大价值,直接实现是 \(O(n^2\log S)\) 的,求删去每个数的答案可以再对后缀 dp 一遍拼起来,当然可以通过一些手段去掉 gcd 的 \(\log S\)。
记 \(w(x,y)=\lfloor\frac{S}{\text{lcm}(x,y)}\rfloor\times\text{lcm}(x,y)\),则可以发现 \(w(x,y)\) 其实就是 \(S\) 内最大的 \(\text{lcm}(x,y)\) 的倍数,所以还可以写成 \(w(x,y)=\max_{z\le S,x\mid z,y\mid z}z\)。
考虑数据随机的做法,随机的性质是 \(S\) 以内 \(a_i\) 的倍数期望是 \(O(\log S)\) 的,所以说你可以开一个数组 \(val_i\) 表示当前满足 \(a_x\) 是 \(i\) 的因数时 \(f_x+i\) 的最大值,这样转移和修改的时候都可以枚举自己的倍数,复杂度 \(O(n\log S)\)。
考虑值域不大的做法,这时候记 \(val_i\) 表示当前满足 \(a_x=i\) 时 \(f_x\) 的最大值,转移直接枚举上一个值是多少即可,复杂度 \(O(n|a|)\)。
上面两个做法在合并前后缀的时候都可以通过记录上一个和上上个位置的方式合并,因为跨过更多位置的情况不需要考虑。合并的形式是若干次区间 chkmax,最后查询整个序列,这里我们大概是需要把前者的速度做到 \(O(1)\) 的,所以使用猫树或者 ST 表即可。
对于一般的情况,考虑结合上面的两个做法。根号分治,设立阈值 \(B\),称 \(a_i\le B\) 的点为小点,\(a_i>B\) 的点为大点。转移时,如果相邻两个位置其中一个是小点,可以直接枚举这个点的值来转移,复杂度 \(O(B)\)。对于大点向大点转移的情况,沿用数据随机的做法,此时倍数只有 \(O(\frac{S}{B})\) 个。平衡一下,取 \(B=\sqrt S\) 就可以做到 \(O(n\sqrt S)\),但是常数太大了根本过不去。
考虑卡常,注意到一个性质是如果 \(x,y\le B\) 那么 \(w(x,y)\ge\frac{S}{2}\),所以对于一次转移 \(f_i\to f_j\) 如果 \(i\) 和 \(j\) 之间间隔了不小于 \(3\) 个小点这种转移没用,因为不如把这些小点全选上。此时我们把小点情况的转移和合并都优化到了 \(O(1)\),虽然总复杂度还是 \(O(n\sqrt S)\) 不变,但是注意到原来乘除法和寻址的常数瓶颈都在刚才优化掉的小点的部分,所以现在跑得比之前快得多了,可以通过。
P10433
实现了一下 Alan_Zhao 老师讲的做法,直接拿下了最优解!
首先进行一点简单的分析,每个玩家的移动策略是完全独立的,也就是说除了第一个玩家其它人只需要最小化自己每轮的移动步数之和就可以了。容易发现策略其实只有两种:
- 移动到最近的一个黑点上去,以后每轮花费两步选一个邻居走过去再回来。
- 移动到一个邻居有黑点且自己是黑点的点上去,以后每轮在这两个黑点之间移动,每轮花费一步。
将从 \(u\) 出发走 \(x\) 轮的最小步数记为一个函数 \(f_u(x)\),可以发现我们肯定是轮数小的时候用第一种策略,轮数大的时候用第二种策略。所以 \(f_u(x)\) 的图像长成两段一次函数,第一段斜率为 \(2\),第二段斜率为 \(1\)。
那是不是求出离每个点最近的黑点/一对相邻黑点,就能求出每个点对应的分段函数具体是啥了?并不是,注意使用第二种策略时路径上可能会有其它黑点,那咋办。再冷静分析一下,假如一条这种路径长度为 \(d\),上面的黑点个数为 \(c\),我们可以直接认为轮数为 \(t\) 时这条路径的步数为 \(d+t-c\)。
为什么呢?如果 \(t\ge c\) 这么算没问题,但是 \(t<c\) 呢?由于此时路径上一定没有其它相邻黑点,发现这个式子只会把距离算大,那这时候用第一种策略的式子计算就能保证不会算错了。所以对第二种策略,我们只需要找到一条到某对相邻黑点的路径使得 \(d-c\) 最小,可以把边权设为 \(1\) 并给黑点带上 \(-1\) 的点权 BFS 得到。
现在我们可以表示出 \(f_u(x)=\min(x+b_1,2x+b_2)\)。考虑设 \(g(x)=\sum f_u(x)\)(对除了第一个玩家外每个玩家的起始点 \(u\) 求和),由于凸函数相加还是凸函数,所以 \(g(x)\) 的图像会是上凸的。容易提前使用二阶差分快速预处理出来每个 \(g(x)\) 的值。如果我们记 \(d(u,k)\) 表示从第一个玩家的起始点出发,走到过 \(k\) 个黑点时到 \(u\) 的最短路,则答案 \(F(u)=\min_{k=0}^n(d(u,k)+g(k))\)。
为了求出所有的 \(F(u)\),考虑从第一个玩家的起始点出发,执行一个类似 SPFA 的过程。具体来说开一个队列,队列内存储若干状态 \((u,k,d)\) 表示当前走到了 \(u\),走到过 \(k\) 个黑点,路径长度为 \(d\)。取出一个状态时,枚举 \(u\) 的一个邻居 \(v\) 并尝试用 \(d+1+g(k)\) 去更新 \(F(v)\),如果更新成功了就把新的状态加入队列。
看上去非常暴力,但是提交上去发现直接通过了本题,而且跑得飞快,这里是提交记录。
这样做的复杂度也许确实是对的。观察 \(F(u)\) 的表达式,发现对于用来更新它的若干 \((k,d)\),只有在下凸壳上的点才可能更新成功。坐标系大小是 \(O(n)\) 的,所以凸壳上的点数也就只有 \(O(n^{\frac{2}{3}})\),那每个点在 SPFA 中应该也只会被松弛这么多次,所以理论上这个做法的复杂度是 \(O(n^{\frac{5}{3}})\)?
更优越的是这个做法代码也无敌好写。
P12865
是不是之前有一场 pjudge 考了一模一样的东西。
把区间和差分成前缀和,容易证明前缀 \([1,l]\) 进行 \(k\) 次冒泡排序后的和是 \([1,l+k]\) 的前 \(l\) 小数之和,主席树即可。
NFLS 期望与概率专题
B
根据对称性只需要算 \(P(a=b)\) 即可。
然后随便乱推一下,答案是 \(\frac{3}{n(n+2)}\)。
D
第一问是容易的,每次操作都会让叶子总深度加 \(2\)。
第二问,考虑期望拆成 \(\sum_iP(x\ge i)\)。设 \(dp_{i,j}\) 表示当前有 \(i\) 个叶子深度都 \(\ge j\) 的概率,枚举下根的一棵子树有多少个叶子转移即可。
E
首先合法的 \((a,b,c)\)(对应三种血量怪物数量)数量不多,只有 \(170\) 种不到。
于是有个暴力 dp,设 \(f_{i,a,b,c}\) 表示当前是第 \(i\) 轮,三种血量怪物数量是 \(a,b,c\),到结束的期望。
方便的推法是倒着转移,然后矩阵快速幂优化即可。
F
首先 \(n\) 个点的二叉树数量就是卡特兰数,众所周知的。
\(n\) 个点的二叉树叶子数量之和,其实是 \(n\) 乘上 \(n-1\) 个点的二叉树数量。
这是因为,一棵 \(n\) 个点的二叉树剥掉任意一片叶子都会得到一棵 \(n-1\) 个点的二叉树,而一棵 \(n-1\) 个·点的二叉树恰好有 \(n\) 个地方能够挂新叶子,所以每棵 \(n-1\) 个点的二叉树对应了 \(n\) 个叶子。
剩下的部分幼儿园小朋友都会了。
G
拆位高斯消元。
H
首先求最小步数有个贪心策略,倒着扫每次遇到亮的灯就按即可,正确性显然。
开始的时候先跑一下上面的贪心,设我们按下的位置集合为 \(S\),我们惊奇地发现无论如何最后一定是 \(S\) 内的位置按了奇数次,\(S\) 外的位置按了偶数次,否则一定不合法。
然后就可以考虑 DP 了,设 \(f_{i}\) 表示目前还需要按奇数次的位置有 \(i\) 个,期望按多少次变成 \(i-1\) 个,列完式子后简单解下方程即可。
I
年初的时候我声称我对无项连通图计数已经有感觉了,这题告诉我其实完全没有。
直接对着给的提示做,考虑 Kruskal 的过程,假如最后一条加入并查集的边在所有边排名是 \(i\),计算这样的概率乘上 \(\frac{i}{n+1}\) 求和就能得到答案了。现在的问题形如,求加入 \(i\) 条边使得图刚好连通的概率,也就是【加入 \(i-1\) 条边图不连通的概率】减去【加入 \(i\) 条边图不连通的概率】。
然后设计 DP,\(f_{S,i}\) 表示加了 \(i\) 条边 \(S\) 内不连通的概率,对称地记 \(g_{S,i}\) 表示连通的概率,显然两者之和固定。当然,额外限制一下加的边端点必须都在 \(S\) 内。
转移大概就是,枚举包含了 \(S\) 的 lowbit 的一个子集 \(T\),表示其所在的连通块,有 \(\sum_{i}f_{T,i}\times\dbinom{E_{S/T}}{x-i}\to f_{S,x}\)。
直接暴力实现这个 DP 当然也可以通过,但是有意思的是发现这个每次转移都是卷积状物,把第二维看成多项式的话最后的次数不超过 \(m\),带入 \(m+1\) 个点值进去最后拉插,复杂度可以做到 \(O(3^nm)\)。
J
从小到大枚举 \(S\) 分层高斯消元。
L
同样地枚举一个 \(v\),这里发现 \(P(x\le v)\) 看上去是容易计算的。
把 \(\le v\) 的数看成 \(0\),大于 \(v\) 的数看成 \(1\),相当于每个询问区间内至少有一个 \(0\)。
先对区间的结构调整一下,包含的可以直接删掉,这样区间右端点随着左端点递增也是递增的。
我们设 \(fl_i,fr_i\) 为最左/最右能覆盖到 \(i\) 的区间位置,发现这两个端点也是单调的。
问题形如,选择若干个 \(i\) 使得这些 \([fl_i,fr_i]\) 并起来为 \([1,n]\)。直接对着 DP,设 \(f_{i,j}\) 表示最后一个选的位置在 \(i\) 且选了 \(j\) 个的方案数,转移枚举上一个选的位置 \(k\),要求 \(fr_k+1\ge fl_i\) 即可,可以双指针+前缀和优化。
这个 DP 的总复杂度是 \(O(n^2)\),跑出来一遍后统计答案是容易的。
8.12 C
我们考虑用直观且人类的方式来刻画一下。
要计算一个点 \(u\) 的答案时,先把 \(u\) 提为根。考虑给树上的边填一个长度为 \(n-1\) 的排列,表示删边的顺序,发现一条边上的数如果是到根路径上的 \(\max\) 就会给答案乘 \(\frac{1}{2}\)(相当于必须往根的方向缩),否则给答案乘 \(1\)。我们求出所有排列对应的答案之和,再除以 \((n-1)!\) 即可。
直接 DP 是考虑记 \(f_{u,i}\) 表示 \(u\) 子树内边权是到根的 \(\max\) 的边有 \(i\) 条时的答案,发现这样没法合并,具体来说确定了一个点父亲边权在子树内的相对排名之后,子树内原先会造成贡献的一些边会废掉。那就直接给状态加一维变成 \(f_{u,i,j}\),\(j\) 表示的是我们忽略掉子树内前 \(j\) 小的边(可以理解为是钦定它们不造成贡献)。
当然这样复杂度有点爆,发现 \(i\) 这一维可以直接砍掉,我们只需要转移的时候在 \(i\to i+1\) 时乘个 \(\frac{1}{2}\) 的系数即可。现在状态是 \(f_{u,j}\),转移大概是个树背包的形式,跑一遍是 \(O(n^2)\) 的,每个点跑一遍就是 \(O(n^3)\) 的,稍微有点卡常。
8.12 D
made 调不出来了啊啊啊啊啊,开摆了。
大体记一下,就是你先把操作序列 reverse 一下,这样是每个区间会被前面的分割线劈开。考虑如何计算一个区间的答案,记录下每个位置作为分界线时最晚的操作时间,发现就是一个在笛卡尔树上走的过程。对于特殊性质,在笛卡尔树上每个点预处理这个区间每个前缀和每个后缀的答案,则容易处理询问。具体的预处理方法大概是(以前缀为例),左儿子前缀的答案可以继承过来,然后设完整左区间的答案为 \(len\),右边的答案 \(x\) 会变成 \(\frac{x+len}{2}\),使用线段树可以维护,但是非常慢。可以考虑启发式合并,维护一个一次函数标记即可。
没有特殊性质的情况,考虑操作分块,前面处理过的整块直接把笛卡尔树建出来,有询问先遍历当前块内的分割点裂成 \(O(B)\) 个区间,这样每块相当于有 \(O(n)\) 次查询。发现直接做就是 \(O(n\sqrt n\log n)\) 的,对完了。精细实现可以去半个或一个 \(\log\)。
然而好像有爆标的 polylog 做法,可以做到 \(O(n\log^2 n)\) 甚至 \(O(n\log n)\),有点神。
ARC142D
这种树上构造题其实还挺有意思?
首先考虑距离和应该是多少,这个非常经典。固定根时,对于树上的一条边 \(u\to v,fa_v=u\),它对答案贡献的上界就是 \(\min(siz_v,n-siz_v)\)。找到重心 \(rt\),以它为根时答案上界就是 \(\sum_{u\not= rt}siz_u\),只需要每次匹配的点来自 \(rt\) 两个不同儿子的子树即可取到(最后一次匹配会是 \(rt\) 和某个儿子),那就先猜测一定能构造到上界。
接下来考察下完美匹配的限制。一个显然的事实是一棵树如果存在完美匹配那一定是唯一的,从叶子向上构造即可说明。发现对于 \(siz_u\) 为奇数的 \(u\),\(u\) 只能和父亲匹配;对于 \(siz_u\) 为偶数的 \(u\),儿子里一定唯一存在一个 \(v\) 使得 \(siz_v\) 为奇数(否则不存在完美匹配),\(u\) 只能和 \(v\) 匹配。
可以发现一个事实是,存在完美匹配等价于子树大小为奇数的点数等于子树大小为偶数的点数。下面如果 \(siz_u\) 为奇数则称 \(u\) 为奇点,否则称 \(u\) 为偶点。
删去一个叶子会使得它到根每个点的奇偶性发生变化,所以每次匹配其中一个点一定来自 \(rt\) 匹配的点的子树,也就是其唯一奇儿子的子树。为了保证匹配完,另一个点当然会选择其它子树里 \(siz\) 最大的子树。进一步地,这两个点到根路径上的所有点都必须是奇偶交替的,否则删完后就不合法了。
问题就在如何快速找到这两个点了,事实上可以对每棵子树提前 DFS 来预处理出一个删点顺序。具体来说,考虑当前的一个点 \(u\):
- 如果 \(siz_u\) 为奇数,那所有儿子 \(v\) 都满足 \(siz_v\) 为偶数,对儿子子树内的删点顺序没有限制。
- 如果 \(siz_u\) 为偶数,那只有唯一的儿子 \(v\) 满足 \(siz_v\) 是奇数。发现肯定是先删 \(v\) 子树内的点,删完后 \(siz_u\) 就会变成奇数,回到了上面的情况。
解决了找点的问题就直接模拟上面的策略就好了,可以简单地做到 \(O(n\log n)\)。
ARC184B
别说还真挺难。
判掉些显然的情况,比如 \(b\) 里面有 \(a\) 没出现的数,两个序列长得完全一样。
当 \(k=1\) 时,把 \(b\) 序列的连续段缩起来,判断 \(a\) 的子序列里有没有缩完后的连续段即可。
\(k>1\) 时,我们可以做到交换两个相邻的数字,前提是有可以浪费的位置。如果 \(b\) 缩完后长度不是 \(n\) 显然一定合法,否则如果存在 \(|i-j|\le k\) 且 \(b_i=b_j\) 也合法,其它情况都无解。
ARC184E
首先对每个 \(i\) 求出 \([l_i,r_i]\) 表示从 \(i\) 开始向两侧扩展出的极长区间,满足 \(\forall j\in[l_i,r_i]\),\(b_j\) 在 \(a_i\) 子树内。根据树的性质,这些区间要么不交要么包含,区间之间也是树的关系。
但是 \([l_i,r_i]\) 并不是 \(i\) 能移动到的位置,可能移动过程中被堵住。发现一个区间会堵住外面的数当且仅当 \(siz_i=r_i-l_i+1\),也就是子树内刚好填满区间。所以就可以从下往上做,开个 set
维护下会堵住移动的区间端点,求出当前区间实际能移动的范围减去区间内要用掉的位置数,乘起来就得到答案了。细节就是没保证 \(a_i\) 互不相同,需要除以一些阶乘。复杂度 \(O(n\log n)\)。
ARC182C
感觉非常非常简单啊,不懂为什么评分会这么高。
令 \(X=\prod p_i^{q_i}\),众所周知约数个数就是 \(\prod(q_i+1)\)。考虑拆组合意义,每个 \(q_i\) 再写成序列里所有的数在 \(p_i\) 上的次数之和,相当于是每个质因子选择序列里一个数在上面的指数乘起来求和。考虑 \(16\) 以内的质数只有 \(6\) 个,状压一下,设 \(f_{i,S}\) 表示填了 \(i\) 个数当前已经选好了 \(S\) 内质数对应括号的贡献,显然可以写成矩阵的形式,复杂度 \(O(T^3\log n)\),其中 \(T=65\)。
ARC182D
主播主播这也太吃脑子了。
考虑去掉取模,变成 \(\forall i,a_i\not=a_{i+1}\land|a_i-a_{i+1}|<m\),此时只要 \(\forall i,a_i\equiv b_i\bmod m\) 即可。
发现操作的过程中 \(a_i\) 之间的相对大小不会改变,假如最终序列为 \(a'\),确定了 \(a'_1\) 就可以直接还原整个 \(a'\)。
可以发现对于固定的 \(a'\) 操作步数下界为 \(\sum|a_i-a'_i|\),这个下界显然能取到。不妨先认为 \(a'_1=b_1\) 然后直接构造出 \(a'\),此时的问题变成了找到 \(k\) 使得 \(\sum|a_i-a'_i-km|\) 最小,显然 \(km\) 会取到 \(a_i-a'_i\) 的中位数附近,复杂度 \(O(n\log n)\)。
CF662C
枚举行的翻转情况 \(S\),设 \(T_i\) 为第 \(i\) 列的初始状态,答案为 \(\sum\min(|T_i\oplus S|,n-|T_i\oplus S|)\)。
令 \(g(A)\) 表示初始有多少 \(i\) 满足 \(T_i=A\),\(w(A)=\min(|A|,n-|A|)\),相当于对所有 \(S\) 询问
发现其实就是询问 \(\sum_{A\oplus B=S}w(A)g(B)\),直接 FWT 即可,复杂度 \(O(2^nn^2)\)。
事实上这个做法对 \(w\) 函数没有要求,所以应该可以处理很多形如查询 \(\sum w(T_i\oplus S)\) 的问题?
UOJ310
考虑一个异或和为 \(0\) 的子集 \(S\),发现会对答案造成 \(2^{|S|}\) 的贡献。
发现就是每个位置 \(i\) 写成一个多项式 \(F_i=(1+2x^{a_i})\),求所有多项式乘起来之后零次项系数。
我们要求 \(\prod_i\operatorname{FWT}(F_i)\)(这里是点乘),注意到 \(\operatorname{FWT}(F_i)\) 只会有 \(-1,3\) 两种数,考虑用一些手段手算 FWT。
考虑先求 \(G={\rm FWT}(\sum F_i)\),对于 \(G\) 的某一项 \(p\),设所有 \({\rm FWT}(F_i)\) 在第 \(p\) 项上 \(-1,3\) 的个数分别为 \(x,y\)。
发现我们有 \(x+y=n\land x+y=[x^p]G\),所以可以直接解出来 \(x,y\),于是 \([x^p]\prod_i{\rm FWT}(F_i)=(-1)^x3^y\)。
这样我们只需要跑一次 FWT 就解决了本题,复杂度 \(O(V\log V+n)\)。
CF1119H
我们直接考虑本题和上题的一般情况,其实就是求形如 \(\prod_{i=1}^n\sum_{j=0}^{m-1}d_jx^{a_{i,j}}\) 这种式子。
延续下上面的想法,令 \(f_i=\sum_{j=0}^md_jx^{a_{i,j}}\),则 \({\rm FWT}(f_i)\) 的取值会有 \(2^m\) 种,具体来说就是 \(\sum_{i=0}^m s_id_i\),其中 \(s_i\in\{-1,1\}\)。
考虑 \(F={\rm FWT}(\sum_{i=1}^nf_i)\),同样地我们希望对于 \(F\) 中的每一项 \(p\),求出上面 \(2^m\) 种取值各有多少次。
设 \(c_k\) 表示取值组合 \(k\) 的出现次数,其中 \(k\) 的二进制第 \(i\) 位如果是 \(1\) 表示 \(s_i=-1\),否则 \(s_i=1\)。
我们仍然希望利用解方程的方式求解所有的 \(c_i\),这里自然需要 \(2^m\) 个。考虑所有的 \(T\subseteq\{1,2,\ldots,m\}\),设 \(z_{i,T}=\oplus_{j\in T}a_{i,j}\),多项式 \(h_T=\sum_{i=1}^nx^{z_{i,T}}\),记 \(H_T={\rm FWT}(h_T)\)。发现 \([x^p]H_T=\sum_{i=1}^n(-1)^{|p\& z_{i,T}|}=\sum_{i=1}^n\prod_{j\in T}(-1)^{|p\& a_{i,j}|}\)。
对于一个 \(c_k\) 来说,因为如果 \(k\) 一个 \(j\in T\) 的位 \(j\) 为 \(1\) 则代表 \((-1)^{|p\&a_{i,j}|}=-1\),所以一个 \(c_k\) 对应的系数序列出现一次贡献就是 \((-1)^{|p\&T|}\),即 \([x^p]H_T=\sum_{i=0}^{2^m-1}(-1)^{|i\&T|}c_i\),发现把所有 \(H_T\) 的 \([x^p]\) 拉出来 IFWT 回去正好得到所有的 \(c_k\)!!于是答案就是好求的了,最后的复杂度是 \(O(nm2^m+(m+k)2^{m+k})\)。
8.19 A
对每个数建一棵以它为倍数的数的 Trie 即可。
8.19 B
只会经过树上路径上的路线,不会往外跳,可以先倍增逼近下 LCA。
最后一步有点小问题是需要判断是否存在一条路径直接跨过 LCA 包含两个点,二维数点判一下。
8.19 C
每条边写成一个集合幂级数 \(\prod(0.75+0.25x^w)\),只需要求所有生成树边权乘积之和。
处理一条边的集合幂级数,只需要套用 UOJ310 的方法,先加起来解方程即可。
8.19 D
有一个贪心的策略就是,对于当前的音符,如果一只手能接到就不进行任何移动,否则移动其中一只手到 \(p_i-L\) 或 \(p_i\) 中较近的那一个,因为更多的移动完全可以拖到以后再说。
设 \(f_{i,0/1,j}\) 表示当前一只手在 \(p_i-L/p_i\),另一只手在 \(j\) 的最小代价。这个状态在 \(i\) 和 \(i+1\) 之间转移是不好做的,我们直接向更靠后的位置转移。具体来说设 \(to_i\) 表示 \(i\) 后面第一个这只手接不到的音符的位置,转移可以简单分为:
- \(j\) 能接住 \(to_i\),那就再找到 \(to_j\) 表示后面第一个两只手都接不到的,枚举移动哪只手直接转移即可。注意到这种转移一共只有 \(O(nL)\) 次,直接暴力做复杂度就是能接受的。
- \(j\) 接不住 \(to_i\),并且我们移动 \(j\) 这只手。发现只会转移到 \(O(1)\) 个状态,只需要对当前的 \(f_{i,0/1,j}+j/f_{i,0/1,j}-j\) 维护个前后缀 \(\min\)。
- \(j\) 接不住 \(to_i\),并且我们移动 \(i\) 这只手。发现 \(j\) 这一维会做整体加,然后要整体做个对位 chkmin。
直接动态开点线段树维护 DP 数组即可,第一种暴力单点修改,第二种直接线段树上维护信息,第三种做线段树合并。
复杂度 \(O(nL\log n)\),跑得飞慢。
CF983D
非常好数据结构题。
肯定要扫描线,线段树上套个 set
维护下完全覆盖了这个点的矩形颜色。一个颜色能被看到当且仅当,存在一条从叶子到根的路径使得这个颜色一路上都是最大值,所以可以在线段树上维护个 \(mn\) 表示到子树内某个叶子的路径最大值的最小值,这样和到根的最大值比较下就能知道一个颜色能不能被看到了。
问题是我们还会删除矩形,看不到的可能有能看到了,那咋办。显然颜色越大越容易被看见,那就每个点再维护个当前没被看到的最大颜色是什么,再对这个维护个子树最大值,这样就能快速判断一个线段树子树里有没有新的能被看到的矩形了。每次删除操作结束后直接根据这个遍历,可以以 \(O(\log n)\) 的代价找到一个新的能看到的颜色,所以复杂度是对的。
最后总的复杂度就是 \(O(n\log^2 n)\)。
AGC061C
考虑给操作序列加点限制让它和最后的排列一一对应,发现这里可以直接取字典序最小的,也就是不改变相对顺序的前提下能选左就选在左边。那对于任意的一个操作序列,不合法当且仅当存在一个选了右的区间,满足区间内没有其它点。
对最后一个条件容斥,每个人有三种选择:选左或者右贡献 \(1\) 的系数,或者钦定区间内没人并选后贡献 \(-1\) 的系数。
如果一个人 \(i\) 选了第三种决策,则可以直接确定一个区间 \([L_i,R_i]\) 内的人的决策。发现这些区间有交的时候答案为 \(0\),所以只需要考虑五交的情况。于是直接顺着 DP 考虑每个人的决策,\(i\) 做第三种决策时直接让 \(f_{L_i-1}\) 转移到 \(f_{R_i}\) 即可,复杂度 \(O(n)\)。
P10145
神题!自己做的时候好像只想到了特殊性质,注意力还是太不够了。
首先选了一个区间 \([L,R)\) 就直接连边 \(L\to R\),则一个区间 \([l,r)\) 的和能确定当且仅当 \(l,r\) 连通,证明显然。
于是我们可以简单地做特殊性质,设 \(f_u,g_u\) 表示 \(u\) 子树内 \(L_u,R_u\) 是否连通的方案数。
研究下图有什么性质,发现这些边只会在端点处相交,否则都是包含或者不交的。可以发现只考虑一个子树内的时候,(如果 \(L_u,R_u\) 不连通)和 \(L_u\) 连通的点一定是一段前缀,和 \(R_u\) 连通的点一定是一段后缀,否则肯定会破坏上面的性质。于是可以这么设计状态:设 \(f_{u,i}\) 表示 \(u\) 子树内 \(L_u,R_u\) 不连通且和 \(L_u\) 连通的点最大编号为 \(i\),\(g_u\) 表示 \(u\) 子树内连通。
转移是容易的,不详细列了。但是有一点需要注意,对于 \(f_{ls,i}\times f_{rs,j}\to f_{u,i}\) 这种转移,\((i,j]\) 内的点将不可能再与外面连通,我们需要保证限制的区间要么被 \((i,j]\) 包含,要么包含了 \((i,j]\)。这个可以考虑异或哈希处理,这样就变成了当 \(i,j\) 的哈希值相同时,可以执行这种转移。现在得到了一个多项式做法。
最后一步,发现第二维不再关心具体位置了,只关心哈希后的等价类,把哈希值离散化一下替代第二维,上面提到的那种转移就变成了 \(f_{ls,i}\times f_{rs,i}\to f_{u,i}\) 了。列出来所有转移后发现可以简单线段树合并,于是做到了 \(O(n\log n)\)。
P8329
有空回来复习下这题。
ARC127E
首先显然可以转成对删除的集合计数,然后考虑确定要删哪些数后的最优策略。
每次 pop 显然可以让它弹出目前最后被 push 进去的数,这样你就不用管 pop 操作了,变成每次要么选一个数扔进堆里,要么选一个没选过且大于堆里所有数的数删掉。显然没删的数和删的数在选的时候都会升序选择,否则调整不劣。同时对于一次删数操作,假设前面有 \(cnt\) 个加数操作,则这次删数的取值范围会被限定在 \((cnt,n]\)。
现在的问题变成,有一个序列 \(lim\),求有多少递增序列 \(x\) 满足 \(x_i>lim_i\),随便 DP 就是 \(O(n^2)\) 的了。
QOJ9611
首先看下题目的这个式子,看上去是比较有转化空间的。当然直接对着硬算好像也有一些高复杂度的容斥做法,可以参考官方题解,这里略去。假设现在 \(m\) 个排列都是确定的,考虑所有序列 \(b_{1},b_{2},\ldots,b_{n}\),满足 \(\forall 1\le i\le n,b_i\in[1,n]\) 且 \(\forall 1\le j\le m,p_{j,i}\ge b_i\),发现符合这些限制的 \(b\) 序列的数量正好就等于 \(\prod_{i=1}^n\min_{j=1}^mp_{j,i}\)。
交换下求和顺序,对所有满足 \(b_i\in[1,n]\) 的序列 \(b\) 计算有多少排列组满足第 \(i\) 列的数都不小于 \(b_i\)。这样的好处是,此时限制对每行排列都是独立的,于是完成了对题目的第一步转化。
假设 \(b\) 序列是确定的,对于某一行排列,按照 \(b_i\) 从大到小的顺序填数每次能填多少数其实是固定的,方案数可以直接算出来。具体来说,把 \(b\) 序列降序排序后一行排列的方案数就是 \(\prod_{i=1}^n(n-b_i+1-i+1)\),那考虑所有行直接把它 \(m\) 次方一下就好了。当然这个连乘里要每一项都是正的,否则实际上方案数是 \(0\)。
于是 \(q=0\) 就非常好做了,把上面从大到小插数的过程改成 DP,设 \(f_{i,j}\) 表示 \(b_x\ge i\) 的列 \(x\) 已经被填完,且当前已经填好了 \(j\) 列,转移枚举值为 \(i\) 的列有几个:
答案即为 \(f_{1,n}\),时间复杂度 \(O(n^3)\)。
对于一般的情况,发现 \(q\) 非常小,所以应该是对它状压。称一行/一列为特殊行/特殊列当且仅当,这一行/列内存在被确定的位置,否则为普通行/普通列。延续一下上面的想法,考虑在状态里将普通列和特殊列分开,设 \(f_{i,j,S}\) 表示 \(b_x\ge i\) 的列 \(x\) 已经被填完,且其中有 \(j\) 个普通列和 \(S\) 内的特殊列。注意 \(S\) 内的列里填好的数不能有小于 \(i\) 的,否则状态不合法。
转移时,同样枚举 \(k\le j\) 和 \(T\subseteq S\),从 \(f_{i+1,k,T}\) 转移过来,重点在于此时的转移系数是什么。首先对于普通行的填数方案和上面基本一致,每一行的方案数都是 \(\frac{(n-i+1-k-|T|)!}{(n-i+1-j-|S|)!}\),把它求个 \(m-row\) 次方即可,其中 \(row\) 是特殊行的数量。考虑特殊行的填法,其实也是类似的,唯一的问题是行内之前可能有些确定的不小于 \(i\) 的数,填的时候不能用这些数。设 \(rem_{i,S}\) 表示第 \(i\) 个特殊行在 \(S\) 内的特殊列的位置里有多少数没被确定,\(cnt_{i,j}\) 表示第 \(i\) 个特殊行里被确定的数有多少个不小于 \(j\),完整的转移写出来是这样的:
其中 \(col\) 表示特殊列的数量。
直接实现时间复杂度为 \(O(n^33^qq)\),可以获得 \(80\) 分,这里给出该做法的提交记录。
考虑优化转移的复杂度,观察转移式里的所有系数,发现含有 \(j,S\) 的项和含有 \(k,T\) 的项之间基本都是独立的,只有前面的组合数有一个 \(j-k\)。这样转移时如果固定了 \(k\),只需要对所有 \(S\) 的子集 \(T\) 求出这对 \(k,T\) 对应的系数之和即可。这就是一个子集和的形式,将所有的 \(f_{i+1,k,T}\) 先提前乘上转移系数里含有 \(k,T\) 项,然后对每个 \(k\) 分别做高维前缀和即可。
这样总的时间复杂度降为 \(O(n^22^q(n+q))\),可以通过本题。提交记录,跑得非常快。
AGC043D
按照前缀 \(\max\) 给序列分段后,每段内的数肯定是来自同一组排列的,所以显然不能有长度大于 \(3\) 的段。
注意我们需要把这些段拼成若干长为 \(3\) 的组,所以还要有长为 \(2\) 的段不能比长为 \(1\) 的段多。
发现这就是充要条件,其余情况都是容易构造的,所以直接对着 DP 一下,\(f_{i,j}\) 表示前 \(i\) 个数,当前长为 \(1\) 的段减长为 \(2\) 的段数量为 \(j\) 的方案数,转移枚举加入的段长,系数要考虑按照相对排名插数的方式确定,复杂度 \(O(n^2)\)。
CF708E
感觉很弱智啊,相当于每层的区间都要有交,然后就有个暴力 DP 做法。
拆成所有区间减去无交的,然后随便推推就可以前缀和一下快速递推了,\(O(nm+k)\)。
CF1098F
对反串建立 SAM,设 \(p_i\) 表示后缀 \([i,n]\) 在 parent 树上对应的点,每次询问相当于查询:
这样我们就扔去了字符串的外壳,将本题转化为了一个树上问题。
假设当前的分治中心为 \(rt\),对于一组询问 \((l,r)\),分类讨论:
- 当 \(l,p_i\) 均来自 \(rt\) 的子树内时,如果是来自 \(rt\) 不同儿子的子树那 LCA 就是 \(rt\),否则路径没有跨过 \(rt\),留到更深层的分治计算。先取出来 \(rt\) 子树内的所有点,则这些点的贡献应该是编号小的一段前缀取 \(len_{rt}\),剩下取 \(r-i+1\),直接二分出来这个分界点快速计算即可。注意对每个子树把贡献取反跑一遍来去掉来自同一儿子子树的情况。
- 当 \(p_i\) 在 \(rt\) 子树内但 \(l\) 在 \(rt\) 子树外时,注意到此时的 LCA 就是 \(lca(rt,l)\),和 \(i\) 还是无关的,所以处理方式和上一种情况没有本质区别。
- 当 \(l\) 在 \(rt\) 子树内但 \(p_i\) 在 \(rt\) 子树外时,\(lca(l,p_i)=lca(rt,p_i)\)。假设 \(\min\) 取在左边的式子,得到 \(r-i+1<len_{lca(rt,p_i)}\),也就是 \(r+1<i+len_{lca(rt,p_i)}\),同时结合 \(l\le i\le r\) 就变成了一个二维数点问题,扫描线解决即可。
时间复杂度显然是 \(O(n\log^2 n)\)。
8.21 A
一个路径的答案是黑色连续段个数,直接 DP。
8.21 B
预处理 \(f_{i,c}\) 表示替换 \(s_i=c\) 后答案的增量。
8.21 C
一轮省集讲过了,主席树维护 AC 自动机跑最短路。
8.21 D
一轮省集讲过了,建广义 SAM,维护每个点出现过的串的连续段。
启发式合并,新增连续段的时候更新询问,这里实时维护或者离线都可以。
复杂度 \(O(n\log^2 n)\)。
P9482
这不是纯弱智吗,为啥这能成集训队题的。
钦定 \(rk_i<rk_j\),发现多算的部分都是回文,统计都可以二维数点。
CF103E
忽略掉点数相同的限制就是最大权闭合子图的板子。
给左部点点权加上 \(\infty\),右部点点权减去 \(\infty\),发现直接跑最大权闭合子图就对了,这样一定不会让左边选的更少。
8.22 A
直接单调队列做完了。
8.22 B
就硬着头皮贪心就好了。
8.22 C
链的做法,直接线段树维护整体 DP 即可,或者考虑容斥。
对于环,从最小值位置断环就可以处理了。
P8484
首先有个结论是考虑每只地鼠被锤的次数,随着窗口的滑动是不降的。
也就是被锤的次数可以增量考虑,证明可以考虑建个费用流模型,然后发现和 \(S,T\) 相连的边都不会退流。
于是每次窗口滑动的时候,考虑所有被锤的次数能 \(+1\) 的地鼠,选择其中高度最大的锤过去即可。
假如知道当前局面每只老鼠被锤的次数,如何判断是否合法?线段树维护 Hall 定理即可。
P9528
根据 Hall 定理那套,维护最大匹配只需要求 \(\max_S\{|S|-|N(S)|\}\) 即可。
套到本题,分析一下 \(S\) 肯定是若干段区间,一个区间的邻域自然就是往两边各扩张 \(L\),那每段区间的间隔一定大于 \(2L\),否则中间空隙选上不影响邻域大小。
设 \(sa_i,sb_i\) 是蚂蚁和方糖个数的前缀和,\(p_i=-sa_{i-1}+sb_{i-L-1}\),\(q_i=sa_i-sb_{i+L}\)。
发现问题转化成,交替选择 \(p,q\) 使得和最大。
直接上线段树维护一下,每个结点记录 \(pp,pq,qp,qq\) 表示最左/最右的选择为 \(p/q\) 时的最大值。
修改其实相当于对 \(p,q\) 做若干区间加,惊奇地发现上面维护的类似 DDP 的东西是支持区间加的(加完后答案的变化都很好算),那就直接做完了,复杂度 \(O(n\log n)\)。
P7154
有个显然的 \(O(n^3)\) 做法,枚举 \(s\) 里最小的没匹配的,限制就是它前面的 \(s\) 必须匹配,\(t\) 更大的必须匹配。
为什么我脑子要强行把 \(s,t\) 分开呢????真没救了。
把 \(s,t\) 一起放在数轴上排序,这样就变成了最小的 \(s\) 前面的 \(s\) 必须匹配,后面的 \(t\) 必须匹配。
这样就不需要枚举最小的那个了,只关心前面的 \(s\) 是否都要求匹配,在状态里压成一维 \(0/1\) 即可,\(O(n^2)\)。
CF765G
首先 CRT 的解是具有唯一性的,也就是对于同余方程组
所有的 \(x\in[0,n-1]\) 都唯一对应一个 \(a\) 序列,同理确定了 \(a\) 序列也唯一对应一个数 \(x\)。
对数 \(x\) 定义长度为 \(m\) 的字符串 \(t\),其中 \(t_i(i\in[0,m-1])=[\gcd(x+i,n)=1]\),那就是要求有多少个数 \(x\) 对应的字符串 \(t\) 和题目中给出的串 \(s\) 相同。考虑通过确定 \(a\) 序列来进一步确定 \(t\),具体来说我们发现 \(t_{i}=0\) 当且仅当 \(\exist j,a_j+i\equiv 0\pmod{p_j}\)。
考虑把 \(p_i\) 按照和 \(m\) 的大小关系分类。当 \(p_i>m\) 时,相当于可以任意将 \(t\) 的一个位置钦定为 \(0\),或者什么都不做。显然具体钦定了哪些位置不重要,只需要关心被钦定的位置数量,所以可以直接 dp,设 \(f_{i,j}\) 表示考虑了前 \(i\) 个 \(>m\) 的质因子,当前 \(t\) 有 \(j\) 个位置被钦定成了 \(0\),转移分钦定一个新位置,钦定一个被选过的位置,什么都不干三种情况,这部分复杂度是 \(O(n'm)\) 的。
当 \(p_i\le m\) 时,相当于可以初始选择 \(a_i\in[0,p_i-1]\) 并使得 \(\forall k\ge0,t_{a_i+kp_i}=0\)。因为质因子比较小所以考虑直接爆搜(记忆化一下),每个质因子将选择的位置和对应的同余类覆盖,查一下上面 \(>m\) 时的 dp 数组即可统计答案,这部分复杂度取决于状态数量。
当然这个只有在 \(\min p_i=2\) 的时候状态数才是真的不多,不然还是爆的,过不去。
考虑进一步细分,当 \(p_i\ge\frac{m}{2}\) 时至多只会多钦定两个 \(0\) 出来,具体来说可能有:
- 没有新的 \(0\) 被钦定,无事发生。
- 选择一个 \(x<m-p_i\),将 \(t_x\) 和 \(t_{x+p_i}\) 钦定成 \(0\)。
- 选择一个 \(x\in[m-p_i,p_i)\),将 \(t_x\) 钦定成 \(0\)。
可以进一步优化状态:此时只关心 \([m-p_i,p_i)\) 内 \(0\) 的个数和剩下位置的具体状态,随着 \(p_i\) 增大这个区间只会扩张。
于是同样从小到达爆搜每个质因子,用这个新方法记录状态记搜,手写哈希表即可通过。
CF1483F
和一轮省集 Alan_Zhao 搬的那个题如出一辙啊?其实应该是严格弱化。
枚举长的那个串,这样短的串在它的子串里。枚举短串的右端点,找到 fail 树上最近的祖先(不妨称为这个右端点对应的“最长后缀”),显然另一个串只能在这些最长后缀里取到。有些最长后缀之间可能是包含关系,被包含的也要直接扔掉。
但是这还不是充要的,有些被保留的串在别的位置可能根本不是最长后缀,这时候它还是不合法的。这个可以这么判:我们要求一种串被保留的次数等于其当前在长串的出现次数。出现次数可以 BIT+DFS 序随便判一下。
复杂度 \(O(L\log L)\)。
ABC236H
ARC149E
P5825
QOJ7759
QOJ9222
对于一对 \(i,j\),如果 \(a_i\le a_j\) 并且 \(b_i\ge b_j\) 一定不会 \(i\) 用 \(b\) 买且 \(j\) 用 \(a\) 买。
把 \((a_i,b_i)\) 画在二维平面上,就是存在一条左下到右上的折线,满足折线左上方用 \(a\),右下方用 \(b\)。
题解说对着折线 DP 就行了,但是我不会做了,日啊。
QOJ9254
枚举 \(x\),尝试对每个 \(x\) 计算出所有数出现次数 \(\le x\) 的方案数。
这里考虑容斥,钦定若干个数的出现次数 \(>x\),每钦定一个带上 \(-1\) 的系数。
然后下面一步的 DP 设计是非常巧妙的,如果一个数钦定了出现次数 \(>x\),我们就直接在后面选出来 \(x+1\) 个位置填上它即可。具体来说设 \(f_{i,j}\) 表示当前已经填了 \(i\) 个数,钦定了 \(j\) 种。转移的决策有两种,要么在当前最靠前的没填数的位置填上一个没钦定过的数,要么新钦定一个数并在后面选 \(x+1\) 个位置填它。
注意这个 DP 第二维大小是 \(n/x\),所以复杂度 \(O(n^2\log n)\)。