2024.9.6 近期练习

P5044 [IOI2018] meetings 会议

对于 \(h_i\le 20\) 的数据,我们每个点维护单调栈,其代价为 \(x\) 的时候,取的位置是一个区间。
很显然已经有一个莫队算法,支持区间加,区间查询即可。然而不优。
其实单调栈与笛卡尔树是相似的,考虑建出笛卡尔树。
我们假设就对 \([l,r]\) dp,那么取出最大值,如果在左边取址,那么右边的贡献都是这个最大值。
右边取址同理,设 \(f_{u}\) 表示 \(u\) 子树内最小的代价,转移很容易。
具体地,把 \(u\) 左右儿子拿出来,取 \(\min(f_{ls}+A_u\times \min(siz_{rs},r-u),f_{rs}+A_u\times \min(siz_{rs},u-l))\).
转移方程里有 \(l,r\),非常难办。不妨把 \(l,r\) 放进状态里面。当我们找到 \(l,r\) 的 lca 时,从这里开始 dp。
这样的话,lca 下每个点都只有一侧是被 \(l,r\) 所限制的。
考虑怎么维护 \(f_{u,x},g_{u,x}\) 分别表示 \(l=x\)\(r=x\) 时的答案。同时设 \(h_u\) 表示原来的 \(f_u\).
\(f_{u,x}=\min(f_{ls,x}+A_u\times siz_{rs},h_{rs}+A_u\times (u-x))\)\(g\) 同理。
这样,我们需要支持区间加等差数列的,还要有区间 checkmin 的线段树。
checkmin 是可以换成推平的,不难观察到 \(\min\) 中后者随 \(x\) 增加而减小,二分出其起效的位置,推平。

P10430 [JOISC 2024 Day1] 鱼 3

我们相当于选出一个 \(\sum k\) 最小的递增的 \(\{c_i-kD\}\) 数列。
由于对于固定左端点,右端点的答案是可以增量的维护出来的,所以我们递推右端点做扫描线。
对于加入的一个右端点,我们从后往前扫,使得前面都变成递增,这么做的话复杂度是 \(O(n^2)\)
我们要考虑找性质进行优化。
不难发现,若一个位置减少,导致其前一个位置减少,那么他们以后都会一起减少。
我们可以用并查集把这些同时发生改变的并起来。然后接下来的处理用线段树维护区间和即可。

P2605 [ZJOI2010] 基站选址

你可以求出每个村庄被覆盖,哪些村庄放基站即可做到,那么这是一些区间。
然后我们再设计一个 dp,\(f_i\) 表示上一个是在 \(i\) 处放置的答案。
假设从 \(i\) 转移到 \(j\),那么我们可以知道上一个填 \(i\),区间填到了多少个。
对于左端点 \(\le j\) 且右端点 \(\ge j\) 的我们可以覆盖掉,右端点 \(\le j\) 的我们需要给补偿。
考虑用线段树维护 dp 过程,线段树维护每个 \(j\),从 \(j\) 转移的代价,转移取 \(\max\)
考虑 dp 从 \(i-1\) 递推到 \(i\) 会发生什么。我们从此覆盖不了右端点为 \(i-1\) 的区间。
对于一个区间 \([l,i-1]\),那么若从 \([1,l]\) 转移,代价需要增加,所以区间加即可。

P9546 [湖北省选模拟 2023] 山路长环 / ring

我们先思考一个问题就是什么时候先手会必胜或必败。
先考虑链的情况。简化问题变为只有两条边,如果初始在边上两个点就是无论如何必败,中间必胜。
所以如果是三个边,分讨中间点和边上点的话,无论如何必胜;
如果四条边呢?如果在边上,第一个点和第三个点都是必败态,这与两条边形式相同,所以必败。
后面同理。所以我们得出,一条链的胜负态只与奇偶性有关。
如果是环呢?如何环的大小为奇数,那么先手直接断一条边就赢了;为偶数,谁先断边就输了。
那么双方的操作肯定都是把边减成 \(1\),直到存在一个 \(1\),就输了。
这个直接用线段树维护,秒了。

CF516D

先取出 \(f_x\) 最小的点做根,这个点就是树的中心,然后随着深度加深,\(f_x\) 递增。
于是我们考虑每个子树维护其子树内最大值为某数时的最大连通块数。
因为根节点的 \(f_x\) 总是最小的,所以这样我们就可以钦定了最小值,取能取到的最大值即可。
由于最大值越大,答案越大。同时我们可以把已经不满足条件的最大值弹掉。
所以我们相当于每个点有一个单调队列状物。
还有一种计算贡献的方式是一个点二分出其能贡献到祖先的哪个,直接加上去即可(拆贡献)。

CF1740H MEX Tree Manipulation

观察到,一个点 MEX 值为 \(x\),那么就其子树需要有 \(2^x\) 个节点,所以 MEX 值的上限是 \(\log n\)
我初始的想法是直接暴力往上跳更新。因为一个点只会被更新 \(\log n\) 次。
然而这是错的,因为每个点的 MEX 值不是一直增加的,而是有可能降低。
因为值很小,把值放进状态里。考虑 ddp,使用树链剖分,维护每个点的轻儿子的答案。
我们要维护设 \(tr_{u,i}\) 表示第 \(u\) 个点,重儿子是 \(i\),其 MEX 值是什么。
这个是可以用线段树区间维护的。同时维护一个 \(sum_{i}\) 表示某区间初始是 \(i\) 的答案之和。
考虑更新了一个点轻儿子怎么做,每个点维护其每个值的儿子的个数即可。
这么做是 \(O(n\log^3n)\) 的。可以优化为 \(O(n\log^2 n)\)
但是你向一个集合里插入一个值求 \(mex\),只有两种方案,是/不是原来的 \(mex\)

CF571D Campus

离线。先把两个并查集形成的树建出来,对于一个点的所有儿子,先合并的边排在 dfs 序的前面。
这样子做,所有操作都是对应树的区间操作。
对于一个单点查询,我们考虑在树 \(1\) 中维护出其所有时刻被什么时候被加了多少,
以及在树 \(2\) 中,计算出其最后一次归零是什么时候。
对于树 \(1\) 中每次操作,我们要维护所有时刻,所以得把所有时刻用数据结构维护。
那么,把区间操作差分,递推下标这一维,主席树维护每个时刻的操作。
最后,我们递推时间这一维,对于树 \(2\) 的操作,执行区间赋值即可。
有话要说:维护时间、值这一二维平面,我们对于树 \(1\) 和树 \(2\) 用了两种扫描线的方法:递推时间/下标。
递推时间是常见的,递推下标维护时间还可以想到 P3863。

CF1582F2

由于 \(V\) 不大,设计 dp,\(dp_{x,v}\) 表示 \(a_i\le x\),异或为 \(v\) 是否可行。
状态数为 \(O(V^2)\),如果每个状态只被更新一次就可以了。\(dp_{x-1,v}\) 可以转移到 \(dp_{x,v\otimes x}\) 处。
当我们插入每个 \(a_i\) 时,令 \(a_i=x\),需要枚举每个 \(v\),去更新对吧。但是这样还是需要 \(O(nV)\)
因为一个点取值只有可行和不可行,当我们已经转移过一遍,现在就无需再转移一遍了。
所以,我们对于每个 \(x\),维护一个 vector 表示当前还可以去转移的集合,转移完就删掉。
同时,因为 \(dp_{x,v}\) 表示的是 \(a_i\le x\),所以要记录 \(v\) 时候,\(x\) 当前最小值,转移的时候一并转移了。
这样的话,可以做到每个状态只被转移一次。

CF1400G

拆贡献,枚举最后区间的人数,设为 \(x\),那么把所有包含 \(x\) 的区间拿出来有 \(cnt_x\) 个,贡献是 \(C_{cnt}^x\)
现在有敌对关系,需要容斥掉。由于 \(m\le 20\),这启示我们用容斥原理。
枚举 \(2^m\) 种情况,每种情况是钦定某些人必选,求出区间交集来,在这个交集里这些敌对的人必选。
对于 \(x\) 的每种情况,处理出钦定有 \(y\) 个点必选的答案 \(f_{x,y}=C_{cnt-y}^x\),并求前缀和即可。

P8859 冒泡排序

对于 type1 的操作,我们先考虑如何计算最少操作数。
把数从大到小插入,如果一个数插入在第一位,那么是不贡献的;反之有 \(1\) 的贡献。
不妨设 \(f_{i,j}\) 表示填到 \(i\),贡献为 \(j\) 的方案数。转移显然。
现在考虑 type2 是一个环。那么你要求的就是所有循环同构的排列中最小值为 \(k\) 的排列数。
这非常难处理。所以我们改一下条件,上述贡献为 \(k\) 的条件可以写成前缀最大值有 \(n-k\) 个。
我们要求所有以某位置开头,绕一圈形成的排列,前缀最大值最多有 \(n-k\) 个的方案数。
因为我们要计数一个环排列,所以我们得钦定一个位置。
如果钦定最后一位是 \(n\),就不用绕一圈,也就是求所有后缀的前缀最大值最多有 \(n-k\) 个的排列数。
我们联想到笛卡尔树之类的结构。
所有后缀的前缀最大值最多是笛卡尔树上一条到根的链最多的向左走的边数。称为“左链”的长度。
所以我们考虑笛卡尔树来做一个 dp。对于一个区间,我们枚举其最大值的位置,分成两个小区间。
因为 \(n\) 被我们钦定在最优,花费了一条左走的边,直接求向左走个数最大值 \(= n-k-1\) 的方案数。
所以我们设 \(dp_{len,now}\) 表示长度为 \(len\) 的区间,当前左链的长度最长为 \(now\) 的方案数,转移前缀和优化。
复杂度 \(O(n^3)\)

ARC148E ≥ K

我们看到这种限制可以类比 CF1842H Tenzing and Random Real Numbers。
那么我们把每个点都乘上 \(2\),再减去 \(K\),现在限制也就是相邻位置 \(\ge 0\)
所以现在我们考虑按照绝对值从大到小排序插入。负数插在两个正数间,同时保证负数不会相邻即可。
设当前插入是负数,有 \(cnt\) 个相同的,维护一个变量 \(s\) 表示当前可用的位置,贡献 \(C_{s}^{cnt}\)\(s\gets s-cnt\)
设当前插入的是正数,那么每插入一个就是多一个可用的位置,插板法,贡献 \(C_{s+cnt-1}^{s-1}\)\(s\gets s+cnt\)
我们维护的是像这样 “边界-s-正-负-正-s-正-s-边界” 这种结构。
初始时候 \(s=1\),因为我们是用插入的方法计算,一开始只有 边界-s-边界。
我一开始打算用的是插入 dp。因为我以为插入正数可以合并两个段或接在一个段后面。
但是其实上述两种情况是不存在的,因为这两种情况都重复计数,只要两个正数段之间不插数即可。

P6596 How Many of Them

很容易想到一个 dp 设 \(f_{i,j}\) 表示 \(i\) 个点,有 \(j\) 条割边的方案数。先计算 \(j>0\) 的情况。
计数 dp 需要“围绕基准点构造一个整体”,选 \(1\) 作为基准点,枚举其边双的大小 \(k\)\(f_{k,0}\times C_{n-1}^{k-1}\)
当我们去掉了 \(1\) 所在的边双,会形成若干个边双,我们要算这些答案。
计算 \(g_{i,j,k}\) 表示 \(i\) 个点,\(j\) 割边,\(k\) 联通块连到某个边双的方案数。
若形成了 \(x\) 个连通块,每个连通块可以连到 \(k\) 个点中的一个,还要贡献 \(k^x\)
那么计算 \(f_{i,j}\) 剩下的贡献是 \(g_{i-x,j-x,x}\times k^x\),枚举 \(k\) 并累加即可。
还有 \(f_{i,0}\) 的计算,设 \(h_x\) 表示 \(x\) 个点连通图数量,\(f_{i,0}=h_x-\sum_{j>0}f_{i,j}\)\(h_x\) 可见 P4841 城市规划。
现在我们求 \(g\),围绕基准点构造一个整体,选编号最小的点出来枚举其连通块的属性。
那么 \(g_{i,j,k}=\sum_{p,q}f_{p,q}\times C_{i-1}^{p-1}\times p\times g_{i-p,j-q,k-1}\),也可以看做是做背包。

P5616 [MtOI2019] 恶魔之树

把每个质因数的贡献拆开是错的,因为这样乘起来可能会出现一个位置同时被钦定选与不选。
换句话说,每个质数的贡献不是独立的。
那么,我们考虑 dp。那么,我们需要把所有质因数的状态放进状态里,但是这样复杂度将会炸裂。
注意到,对于每个 \(>17\) 的质数,一个数不可能同时有两个 \(>17\) 的质因数。
我们考虑先计算只有 \(\le 17\) 质数的方案数,直接设 \(f_{c_1,c_2,c_3,c_4,c_5,c_6,c_7}\) 表示第 \(i\) 个质因数次数为 \(c_i\) 方案数。
dp,每次加入若干个相同的数 \(a_i=v\),更新 \(c\) 数组,加上贡献即可。
对于最大质因数 \(>17\) 的,每个数只有一个这样的质因数,考虑把每个数按照其最大质因数分组出来。
我们逐一加入 \(p\),同时维护 \(f_{0/1,c_1,c_2,c_3,c_4,c_5,c_6,c_7}\),表示 \(p\) 的次数,小质数的次数,大质数的积的和的值。
初始的时候,大质数的积的和的值就是一开始的 \(f\) 计算的方案数。
如果当前加入了一个数 \(v\)\(p\) 的倍数,那么如果 \(v\) 选了,且当前没有 \(p\),那么积就要乘上 \(v\),且 \(p\gets 1\)
最后到下一个 \(p\) 的时候,把 \(p=1\) 的加到 \(p=0\) 那里去。
为什么小质数不能逐个加入呢?因为一个数可能有多个小的质因数,而这些质因数的状态又得要记录。
大质因数,只需要记录一个状态就行了,因为这有这一个状态会受影响。 可以说大质数是两两独立。

CF512D Fox And Travelling

如果有环,那么里面的点是无法被选择的。
所以我们考虑树,我们相当于每次选一个叶子删掉。我们可以钦定一个根最后选择,然后做树上背包。
一棵树里最多有一个点是跟某个环有边,其一定是最后才能选。那么我们直接钦定这个点为根。
但是如果没有这样的点会导致算重复。我们如果枚举每个点令其最后选呢?
虽然钦定的这个点将会最后选,但是一棵树不一定选完,怎么办呢?
假设我们选了 \(k\) 个点,那么这 \(k\) 个点会被计算 \(n-k\) 次,所以直接做一下除法就行了。
最后,每棵树都是独立的,最后可以再做一下 dp。
关于如何才能把树给找出来,可以采用拓扑排序,每次删度数为 \(1\) 的点。或者使用 Tarjan 缩环。

P8321 『JROI-4』沈阳大街 2

考虑把 \(A\)\(B\) 放在一起排序,以便 \(\min\) 值的计算。考虑从大到小排序。
dp,设 \(f_{i,a,b}\) 表示当前到第 \(i\) 位,先前还有 \(a\)\(A\) 没有被配对,\(b\)\(B\) 没有被配对,此时的权值和。
转移假设是 \(A\),权值为 \(v\),可以任找一个 \(B\) 配对,贡献乘上 \(v\);也可以不配对,没有贡献。
那么状态数有一点大,怎么办呢?注意到,如果是 \(A\),那么要么 \(A\) 加上 \(1\),要么 \(B\) 减去 \(1\)
所以,\(A-B=sum_A-sum_B\),那么可以把 \(A,B\) 压缩起来成为一维。

P7116 [NOIP2020] 微信步数

我们把每个维度拆开后,我们要求的是第一个到达边界的维度花了多少步。
这启示我们需要拆贡献,我们想要计算每个维度第一个到边界的方案数与贡献。
如果我们枚举这个维度是多少步到达的,然后计算其他维度在这个时间不能到达的位置数乘起来即可。
考虑每个维度预处理出 \(fr_{i},bc_i\) 表示当前周期内一个周期向前/后走了 \(i\) 步最短的时间。默认向右走。
如果 \(w\) 比较小,我们可以直接枚举每个位置,算出其最短到达边界的时间。
考虑 \(w\le 10^9\) 的情况。
注意到,每个位置的答案的周期,是一个时间周期内的位移,周期之间每个答案的差相等且 \(=n\)
前面 \(n\) 个位置与后面的位置需要分别讨论,因为走出的边界可能会不同。前面的位置去暴力即可。
我们枚举当前维度的周期每个位置,随着经过的周期数增加,另外的周期可用位置数是均匀减少的。
这是一个多项式形式的贡献,考虑插值出来即可,细节应该很多。

AGC043C Giant Graph

由于权值的特殊形式,我们相当于要把 \(x+y+z\) 大的都选满。
我们可以看做是从 \(x+y+z\) 最大的开始选择,每次选了之后,其连接的点就不能选。
每条维度分开讨论,考虑把点从小连到大形成一个 DAG,那么讨论每个点的选择情况。
若一个点所有出边存在一个被选,那么就不选;否则就选。
这和博弈转移的形式相同,被选的点就是必败点;不被选的点相当于必胜点。答案是必败点的权值和。
现在把三个维度组合起来,要使用 SG 函数了。计算 \(SG_{1,i}\otimes SG_{2,j}\otimes SG_{3,k}=0\) 时,\(10^{18(i+j+k)}\) 的和。
因为 \(SG\) 的值一定 \(\le \sqrt n\),使用背包。

P7519 [省选联考 2021 A/B 卷] 滚榜

限制是对于第 \(i\) 个滚榜的人,\(b_i\) 不比前面小,\(a_i+b_i\) 比前面的 \(a_i+b_i\) 和后面的 \(a_i\) 都大。
注意我们计算的是 \(a_i+b_i\) 排列的顺序的方案数,而不是 \(b\) 所有取值的方案数。
所以我们考虑对于每种排列,设计一种 \(b\) 的取值的方案,使用的 \(b\) 最小,\(b\) 多的可以加到第一名。
假设当前加入第 \(i\) 个人,那么其 \(b\) 取最小能用的,\(b_i\ge b_{i-1},a_i+b_i\ge a_{i-1}+b_{i-1}\)
其中第二个比较相同的情况要考虑编号大小。
那么设 \(f_{S,id,b_{id},rem}\) 表示已经选了 \(S\) 集合里的人,上一个选了 \(id\) 以及 \(b_{id}\),当前还剩 \(rem\) 的方案数。
考虑枚举下一个选什么来转移,复杂度 \(O(2^nn^2m^2)\),很劣的一个复杂度。
我们考虑减掉一维,如果删掉 \(b_{id}\) 这维呢?
\(b\) 的式子写出来,\(b_{i}=\max(b_{i-1},b_{i-1}+a_{i-1}-a_i+[id_{i-1}<id_i])\),那么我们可以得到 \(\Delta b\)
于是我们考虑费用提前计算,\(\Delta b\) 被计算了 \(n-|S|+1\) 次。
这样状态数 \(O(2^nnm)\),转移 \(O(n)\)
最后我们发现还有 \(a_i+b_i\) 要比后面的 \(a_i\) 都大,而只要 \(a_1+b_1\) 满足了,后面就肯定满足。

P7914 [CSP-S 2021] 括号序列

由题目的数据范围猜测做法应该是区间 dp。
我突然想说一句话,所谓 dp,就是每次遵循一定的规则把任务划分成子任务,而这就叫转移。
我们对于一个区间,思考其究竟如何转移而来。
区间的组合形式有这些:A+A,A+SA,(A),(A+S),(S+A)。
前面两种,考虑枚举其第一个位置使得可以跟左端点构成的区间的匹配,分成子任务计算。
后三者,只是进行了外面套一个括号的操作,只需要计算 \([l+1,r-1]\) 的答案。
一个合法的区间,必须满足左右两端各有左括号和右括号。因为 A 一定是括号括起来的,
所以我们去求 A 的方案数,然后呢再向左、向右扫,计算 SA,AS 的答案。
一个问题:我们要保证枚举出来的第一个位置与左端点是一个括号套起来的,所以还要记录一个数组。
初始条件的话,考虑把所有 (S)和 ()这种先拿出来,他们无法再拆分。

posted @ 2024-09-07 07:51  s1monG  阅读(41)  评论(0)    收藏  举报