CF1442D Sum
很显然可以设 \(f_{i,j}\) 表示当前处理了前 \(i\) 个数组,选了 \(j\) 个数的最大值,然而转移需要 \(O(k)\)。
考虑挖掘题目数据元素非降的性质。猜个结论呢?
因为元素是逐渐变大的,所以越往后选就一定越优。所以,至多只有一个数组没有被选完。
这个很像 NF0921D。考虑分治+背包处理即可。即分治两端最多只有一边是没有选完的。
可能需要开 \(\log\) 层的数组。不要在分治里面传 vector 常数大。
ABC221H Count Multiset
考虑设计 dp \(f_{i,j,k}\) 表示已经填了前 \(i\) 种数,和为 \(j\),填了 \(k\) 个数的方案数,状态数已经爆表。
观察题解发现:集合计数转化为不降序列计数,然后转差分。
合法的差分数组的条件:连续的 \(0\) 不能有 \(m\) 个;首位非 \(0\);\(\sum c_i(k-i+1)\le n\);长度为 \(k\)。
这么做的话可以消去填 \(i\) 种数这一维。
这里还是运用了拆贡献,我们每个 \(i\) 的 \(a_i\) 算贡献,相当于变成每个 \(k\) 把 \(a_i\ge k\) 个数加起来。
所以直接设 \(f_{i,j}\) 表示填了 \(i\) 长度的数组,当前 \(\sum c_i(k-i+1)=j\),此时的方案数。
转移的话考虑枚举当前位填什么,然后前缀和优化转移,而枚举 \(c_i\) 是调和级数的。
因为每种数组长都要计算,所以需要从后往前填 \(c_i\)。
ARC128D Neq Neq
考虑最后答案的形式一定是左右端点不删,所以考虑从左往右填。
设 \(f_i\) 表示前 \(i\) 个区间形成左右端点不删的方案数,从 \(f_j\) 转移到 \(f_i\) 需要判断 \([j+1,i-1]\) 能否删完。
删除某数的条件是前面,后面和其本身三个数互不相同,或只剩他要删除。
所以我们猜测只需要颜色数 \(\ge 3\) 就能删除,并且不存在相邻相同的数。
这个用前缀和优化,再用一个指针维护当前哪里可以转移即可。
证明的话,只要满足上述条件,就一定存在三个互不相同的连续的数,一直删直到只剩 \(1\) 个要删即可。
CF2021D Boss, Thirsty
一个平凡的状态设计就是设 \(dp_{i,l,r}\) 表示前 \(i\) 行,上一行选了区间 \([l,r]\) 的答案,状态数已经爆表。
若上一个区间选的是 \([l,r]\),那么当前区间 \([l-1,l],[r,r+1]\) 中要选一个出来。而不是有且仅选择一个。
一开始看成区间必须要相交而不包含,这犯了致命的错误,其实是可以包含的。
所以这题就简单了,我们考虑转移是选了 \([l-1,l]\) 还是 \([r,r+1]\),那么状态可以只记录 \(l,r\) 中的一个。
相当于把转移相同的并在一起。设 \(f_{i,l},g_{i,r}\) 表示第 \(i\) 行,左端点是 \(l\) 最大、右端点是 \(r\) 最大贡献。
CF1450G Communism
注意到只有 \(20\) 种颜色,这启示我们进行状压 dp。
如果 \(u\) 颜色改变到 \(v\),那么就连一条 \(u\to v\) 的边,最后如果 \(x\) 是答案,那么就存在一个 \(x\) 为根的树。
很简单的状态设计:设 \(f_S\) 表示 \(S\) 里的集合能否连向 \(S\) 外的某个点形成一个子树。
转移有两种:一种是找一个父亲,\(f_{S}\to f_{S+x}\),一种是合并两个集合(父亲待定)\(f_S\wedge f_T\to f_{S+T}\)。
瓶颈在于枚举子集。考虑优化,因为我们只关注可行性,所以只要有一个转移即可。
根据转移的条件我们可以观察到,也就是若 \(S,T\) 对应的区间之间没有空隙,就一定能转移到 \(S+T\)。
具体地,以为两个区间合并后,总的区间长度一定不比两个区间的和大,所以就成立。
利用这个性质呢?也就是我们只让已经连续的状态去转移,即只保留最优的区间。
所以 \(f_T\) 一定由若干连续的区间构成,只有这样的 \(f_T\) 才是最优的转移。
考虑转移,\(f_T\) 转移到 \(f_S\) 的话 \(T\) 是 \(S\) 中元素按照左端点排序后的一段前缀,枚举即可。
我们通过上述性质,使得找到了一种最优的转移,删去了无用的转移。
CF1284E New Year and Castle Construction
对于求 \(\sum_{p\in S} f(p)\),\(f(p)\) 算的是四元集合的个数,不妨把 \(p\) 加入集合里,现在我们有一个五元集合。
取出来这五个点构出的凸包,计算多少种 \(p\),满足条件。
如果其是三角形,贡献 \(2\);如果是四边形,贡献 \(1\);如果是五边形就没有贡献。
所以如何求凸包是三角形/四边形的方案数呢?设他们是 \(cnt_3,cnt_4\),最后答案是 \(2cnt_3+cnt_4\)。
注意到,\(X=C_n^5=cnt_3+cnt_4+cnt_5\),不妨求 \(Y=5cnt_5+4cnt_4+3cnt_3\),则 \(Ans=5X-Y\)。
\(Y\) 的形式非常优美,\(kcnt_k\) 的形式相当于拆贡献到凸包上的每一条边。
所以我们考虑枚举每一条边,其作为某个凸包上一条边,那么剩余的点都在这条边的同一侧。
直接任意选三个点出来,无论组成的凸包的形态,这条边都会被贡献 \(1\)。
现在相当于枚举两个点,然后算其两侧分别有多少点。
先枚举一个点,然后剩下的点按照极角排序,用双指针维护即可。极角的计算使用 atan2(y,x)
函数。
P9129 [USACO23FEB] Piling Papers G
先容斥 \(\le R\) 减去 \(\le L-1\) 的答案。考虑枚举 \(l,r\)。
考虑固定左端点,然后递推右端点的话,既可以放前面,也可以放后面,这是很难办的除非把 \(x\) 记录。
所以不能让当前 \(x\) 在 \(k\) 上对应的位置移动,我们不妨直接固定 \(x\) 的位置就好了。
所以不妨设 \(f_{i,l,r,0/1/2}\) 表示当前对应 \(k\) 上区间 \([l,r]\),大小关系是小于/等于/大于的方案数。
转移是 \(O(1)\) 的,直接枚举下一个填哪里就好了。初始化就是枚举 \(a_l\) 所在的位置。
现在考虑多次询问如何做,不妨直接固定左端点即可,这么做复杂度是 \(O(n^2\log^2 R)\)。
注意如果有位数小于 \(k\) 的位数的是一定小于的,直接算上答案即可。
P8292 [省选联考 2022] 卡牌
转化为:每个卡牌带有若干质因数,每次给出若干种质数,使得这些质数全部被选的方案数。
想到一个题:P5616 [MtOI2019] 恶魔之树。注意到 \(\sqrt{2000}< 45\),而 \(45\) 以下的质数只有 \(14\) 种。
这启示我们什么呢?大于 \(45\) 的质数,每张卡牌都只带有一种,也就是他们之间不会同时出现。
因为小质数很少,所以我们可以考虑容斥,计算钦定不包含 \(s\) 集合的数,而大质数选完的方案数。
容斥的作用使得我们现在只需要考虑大质数都被选。
对于每个大质数因为他们之间独立,所以每种质数如果出现了 \(k\) 次,那么方案就是 \(2^k-1\) 乘起来。
考虑预处理每种 ban 掉的质数,每种大质数出现的次数,然后乘起来即可。
P10220 [省选联考 2024] 迷宫守卫
因为叶子节点的权值每个都不同,所以关于字典序的比较我们只需要比首位即可。
由于 \(w\) 很大,所以我们不能把 \(w\) 放进状态里,不妨设 \(dp_{u,j}\) 表示 \(u\) 子树首位是 \(j\) 最小代价。
现在考虑求 \(dp_{u,j}\)。分讨如果 \(j\) 在左边,我们可以直接唤醒当前的守卫,或者令右边子树首位 \(>j\)。
即 \(dp_{u,j}=\min(dp_{ls,j}+w_u,dp_{ls,j}+\min_{t>j}dp_{rs,t})\)。
如果 \(j\) 在右边,那么必须令当前左边子树首位 \(>j\)。那么 \(dp_{u,j}=dp_{rs,j}+\min_{t>j}dp_{ls,t}\)。
状态数是 \(n\log n\)(令 \(n\gets 2^n\)),转移用归并实现。所以复杂度是 \(O(n\log n)\)。
最后一步考虑贪心,每次选最小的能代价不超过当前 \(K\) 的首位元素。
如果我们确定了首位,那么就相当于确定一条链,分出 $\log $ 个子树,继续递归并确定首位即可。
注意 \(w_u\) 每次不一定是小于了 \(f_{rs,t}\) 就一定会用。因为我们选择了 \(w_u\) 会使得 Alice 在右子树的选择更少,
所以,只有在我们不选 \(w_u\) 的情况下,左子树答案会改变才会选。
即,当左儿子递归完了之后剩余的 \(K\),依然可以支付 \(dp_{rs,t}\),就无需选择 \(w_u\)。
ABC292G Count Strictly Increasing Sequences
我们若要比较两个长度相同的数一定是从前往后进行比较,然后找第一个不同的位置比大小。
所以我们考虑从前往后数位 dp,每次填一列的数,状态的话要存储当前还需要相同有哪些。
状态数太多了,是 \(O(2^n)\) 级别的。这条路可能走不通我们换一种思路呢?注意到只有 \(10\) 种数字。
那么我们在最高位填了 \(0\sim 9\) 依此出现的若干个段,相当于划分了子任务分成若干个区间。
在不同的段之间,大小关系已经确定了,所以有了一个区间形式的 dp。
设 \(dp_{l,r,k,w}\) 表示 \([l,r]\) 后 \(k\) 位满足条件,首位只能填 \(\ge w\) 的方案数,转移填出 \(w\) 的区间即可。
CF1310E Strange Function
注意到答案不是 \(1\) 的 \(k\) 很少,这启示我们对 \(k\) 进行分讨。
当 \(k=1\) 时,很明显答案就是 \(n\) 的分拆数,考虑 \(O(n^2)\) dp 即可。
当 \(k=2\) 时,我们要对结果集合 dp,设我们要求的是 \(c_i\),那么转化为 \(k=1\) 就是重复 \(c_i\) 个 \(j\)。
其中每个 \(i\) 对应一种 \(j\),考虑使 \(\sum c_ij\) 最小,当 \(\sum c_ij\le n\) 就有一定能分配使得最后结果为 \(n\)。
那么只需要 \(c_i\) 大到小排序,\(\sum_{i=1}^{m}c_ii\le n\) 即可,dp 复杂度 \(O(n^{2.5})\),或者差分贡献算也可以。
\(k\ge 3\) 的时候不难发现此时所有数的和是 \(\le 64\) 的,\(64\) 的分拆数很小,直接 dfs 即可。
ARC068F Solitaire
首先依此插入 \(1\sim n\) 一定是形成前面递减-1-后面递增的形式。
考虑删除操作,每一时刻一定是形成一个区间,然后左边或右边去掉一个。
由于第 \(k\) 个删掉的是 \(1\),所以左边或者右边一定有一边全部删完;\(k\) 后面的不用管,贡献 \(2^{n-k-1}\)。
题目转化为 \(2\sim n\) 选 \(k\) 个数组成 \(A,B\) 两个递减数列,且最大的未选的数小于 \(A,B\) 中一个数列最小值。
考虑 dp,设 \(f_{i,j}\) 表示选 \(i\) 个数选出的数最小值为 \(j\) 的方案数,考虑从大到小加入。
我们钦定 \(A\) 序列中存在当前最小值;\(B\) 序列中最小值为 \(\min_B\);未选的数构成集合 \(S\),最大值为 \(\max_S\)。
那么显然的序列 \(B\) 是满足最大的未选的数小于 \(\min_B\) 的序列。
考虑转移,若放到 \(A\) 序列,那么该数必须 \(<j\),那么若 \(k<j\),\(f_{i,j}\to f_{i+1,k}\)。
若放到 \(B\) 序列,发现你只能把 \(\max_S\) 加入,所以 \(f_{i,j}\to f_{i+1,j}\)。
P10375 [AHOI2024 初中组] 计数
一个序列被删完的充要条件是其能被划分为若干个首尾颜色相同的区间,考虑 dp 判定。
我们可能需要 dp 套 dp,状态还需要设一个 \(f\) 数组表示划分是否可行,若 \(f_1\) 合法即计入答案。
我们若在序列开头加入 \(v\),若存在一个位置 \(j\) 满足 \(a_j=v,f_{j+1}=1\),那么 \(f_1=1\)。
颜色等价,那么只需要记录上述 \(v\) 的个数即可,那么设 \(dp_{i,j}\) 表示长度为 \(i\) 的区间有 \(j\) 个合法 \(v\) 方案数。
因为合法 \(v\) 的增加取决于 \(f_{j+1}\),那么还需要记录当前首位的 \(f\) 值。
转移的话比较简单。
AGC061C First Come First Serve
计数题根据套路构造双射,考虑给每个登记序列一一对应一种登记方式。
不妨让每个人都尽量在区间左端点处登记,也即只有在区间内有人登记时才可以选右端点。
设 \(l_i\) 表示最小的 \(j\) 满足 \(b_j\ge a_i\),\(r_j\) 表示最大的 \(j\) 满足 \(a_j\le b_i\)。
考虑容斥,如果钦定 \(i\) 不合法,那么 \([l_i,r_i]\) 的人选择都可以确定下来。
注意到,钦定的所有不合法的人,\([l_i,r_i]\) 都不会相交,因为但凡有 \(i\) 不合法那么 \([l_i,r_i]\) 都一定合法。
那么答案是:\(\sum_S (-1)^{|S|}2^{n-\sum_{i\in S}(r_i-l_i+1)}\)。考虑 dp,设 \(f_i=\sum_{\forall j\in S,j\le i}(-1)^{|S|}2^{n-\sum_{i\in S}(r_i-l_i+1)}\),
加入 \(r_j=i\) 的区间,转移考虑选一个之前的 \(S\) 加入,那么 \(f_i=\sum_{k<l_i}-2^{-(r_i-l_i+1)}f_k\)。
初始的,\(S\) 包含一个空集,所以 \(f_0=2^n\)。
P10104 [GDKOI2023 提高组] 异或图
考虑 \(m=0\) 的做法,注意到如果存在一个没有最高位限制的数,那么从该位之后都随便选。
即其他的数任意选,由于该数什么值都可以得到,可以通过该数调整得到 \(C\)。
考虑枚举这个位 \(d\),\(d\) 必须是最高的,选若干位取消最高位限制。
不妨枚举 \(d\) 求 \(f_{i,0/1,0/1}\) 表示前 \(i\) 个数,是否有位取消最高位限制;当前 \(d\) 异或是 \(0/1\),此时的贡献。
求一次复杂度是 \(O(n\log V)\) 的。尝试转化为 \(m=0\) 的情况,\(b_i\neq b_j\) 的限制很显然需要容斥。
我们可以得到一个简单的 \(O(2^mn\log V)\) 做法,即钦定若干边不满足条件,形成若干连通块计算方案数。
一个连通块里的 \(b\) 相同,如果大小为偶数那么其抵消,贡献是 \((\min a_i+1)\) ;
如果连通块大小是奇数,那么我们取其最小值出来,把所有取出来的数做一遍 \(m=0\) 即可。
考虑优化 \(2^m\),我们可以对于每种连通块划分求出其容斥系数,是 \(Bell(n)\) 级别的。可以过 \(n\le 13\)。
考虑把同样贡献的合并,我们最后贡献是把每个奇数大小连通块最小值拿出来 dp。
考虑直接枚举这些最小值是哪些,所以考虑把 \(a_i\) 从小到大排序,依此加入。
考虑当前填到第 \(i\) 个,那么前 \(i-1\) 个点势必已经划分好,\(a_i\) 可以作为后面的最小值。
设 \(g_{i,S}\),表示填到第 \(i\) 个,\(S\) 前 \(i\) 部分表示之前的最小值集合,后 \(i\) 部分表示后面的已被选的集合。
那么转移的话枚举 \(i\) 所在的集合即可,转移系数是容斥系数乘上偶数大小连通块贡献。
考虑容斥系数怎么算,考虑求 \(f_S\) 表示只考虑 \(S\) 连通块内部边的钦定情况,形成连通块 \(S\) 被算多少次。
转移用容斥减掉不连通情况,\(O(3^n)\)。最后系数就是所有连通块的 \(f_S\) 乘起来,根据乘法原理。
上述是集合划分容斥。