近期做到的一些好题,将题解收录于此,
本贴仍在施工中,由于篇幅问题,部分题目会专门写一篇博客,这里只放链接。
CF1603E. A Perfect Problem
定义一个序列 (b_1,b_2,b_3,dots b_m) 是好的,当且仅当 (min_{i=1}^{m}b_imax_{i=1}^{m}b_ige sum_{i=1}^{m}b_i)。
询问有多少个长为 (n) 的序列 ({a_i}) 满足 (1le a_ile n+1),且其所有子序列都是好的。
view solution
首先可以发现一个序列满足条件当且仅当将它从小到大排序后每个前缀都是好的。不妨设排序后的结果为 (a_1,a_2,dots a_n)。可以直接对着这个判定方式设计一个 (dp) ,枚举 (a_1),然后从小到大填入数字,(f_{i,j,k}) 表示已经填了 (i) 个数,总和为 (j),正在考虑填入第 (k) 个数,据此可以得到一个状态数 (mathcal O(n^4)),转移 (mathcal O(n)),总复杂度为 (mathcal O(n^6)) 的做法。
观察对前缀的限制,长为 (i) 的前缀满足条件就意味着 (a_1a_ige sum_{i=1}^{i}a_ige a_1i),因此有 (a_ige i),同时如果取 (a_i=i) 则必须有 (a_1=a_2=a_3=dots=a_i=i)。
记 (b_i=a_i-a_1),枚举 (a_1),则原限制变成了:
-
(sum_{i=1}^{n}b_ile a_1)
-
(forall iin[a_1+1,n],i-a_1le b_ile n+1-a_1)。
于是将 (dp) 改为从大到小填 (b_i),有转移:
此时第二维 (sum b_i) 变成了 (mathcal O(n)) 的,因此转移总复杂度也就是 (sum_{i=1}^{n+1-a_1}dfrac{a_1}{i}=mathcal O(nlog n)),总复杂度 (mathcal O(n^4sqrt{n}log n))。
考虑优化,注意到当 (a_1le n-2sqrt{n}) 时,有 (b_nge n-a_1ge 2sqrt{n},b_{n-1}ge 2sqrt{n}-1,dots b_{n-sqrt{n}}ge sqrt{n}),因此必然有 (b_1+b_2+dots b_n>a_1),答案为 (0) 可以不用考虑。据此可以进一步将复杂度优化到 (mathcal O(n^3sqrt{n}log n))。
CF1603D. Artistic Partition
定义函数 (c(l,r)=sum_{lle ile jle r}[gcd(i,j)ge l]),再定义函数 (f(n,k)),表示将 (1sim n) 拆分成 (k) 段 ((x_1,x_2],(x_2,x_3],(x_3,x_4],dots,(x_k,x_{k+1}]),满足 (0=x_1<x_2<x_3<dots<x_k<x_{k+1}=n),得到的 (sum_{i=1}^{k}c(x_i+1,x_{i+1})) 的最小值。
(T) 次询问 (f(n,k))。(Tle 3 imes 10^5,kle nle 10^5)。
view solution
首先容易写出一个对 (f) 的暴力转移:
暴力实现这一过程,我们可以 (mathcal O(n^3)) 预处理 (c),再 (mathcal O(n^3)) 进行转移。
考虑分别进行优化,对于 (dp) 转移的部分,(mathcal O(n^2)) 的状态是我们无法承受的,考虑优化:
首先 (c(l,r)) 必定 (ge r-l+1),因为每一对 ((i,i)(lle ile r)) 都会作出贡献。同时当 (rle 2l-1) 时 (c(l,r)) 会取到下界 (r-l+1)。因此当 (kge log_2 n) 时,一定存在一种方法将 (1sim n) 拆分成 (k) 个满足 (rle 2l-1) 的区间 ([l,r]),因此当 (kle log_2 n) 时 (f_{n,k}=n),于是状态数被优化到了 (mathcal O(nlog n))。
对于预处理 (c) 的部分,考虑推柿子:
于是通过预处理 (varphi) 的前缀和 (s(i)=sum_{jle i}varphi(j)) ,有 (c(l,r)=sum_{g=l}^{r}s(r/g)),可以通过整除分块做到 (mathcal O(sqrt{r-l})) 进行依次查询,也可以 (mathcal O(nsqrt{n})) 预处理后 (mathcal O(1)) 查询。
求 (c) 的复杂度已经可以承受了,回到 (dp) 转移的部分,这个 (dp) 显然无法单调队列/斜率优化/数据结构优化,于是考虑决策单调性优化,大胆猜测 (c(l,r)) 满足四边形不等式,事实上确实如此,证明如下:
设 (l_1le l_2le r_1le r_2) ,有:
于是我们可以用分治做法优化 (dp) ,将 (dp) 转移优化到 (mathcal O(nlog^2 n)),最终总复杂度为 (mathcal O(nlog^2n+nsqrt{n}))。
当然也可以不用预处理 (c),在分治的过程中在 ([ql,qr]) 里找 (mid) 的决策点时,可以先 (mathcal O(sqrt{r-l})) 求出 (c(qr+1,mid)),然后就可以容易的递推出其他决策点对应的 (c),复杂度依然正确。
AGC040E Prefix Suffix Addition
有一个长为 (n) 的序列 (a_1,a_2,dots,a_n)。初始所有 (a_i) 都为 (0),可以进行以下两种操作:
- 选定整数 (k(1le kle n)) 与不下降非负序列 (c_1,c_2,dots,c_k),对所有 (1le i le k),令 (a_i) 加上 (c_i)。
- 选定整数 (k(1le kle n)) 与不上升非负序列 (c_1,c_2,dots,c_k),对所有 (1le i le k),令 (x_{N-k+i}) 加上 (c_i)。
问最少进行多少次操作使得最后对任意 (i) 有 (x_i=A_i)。(nle 2 imes 10^5,A_ile 10^9)。
view solution
首先前后缀操作显然可以转化为对原序列任意一个区间,增加一个不下降序列或不上升序列。
另一个重要结论是:存在一种最优方案使得同一种操作的所有区间互不相交。证明考虑调整法,对于相交的两个区间,直接将相交的部分叠加到某一个操作上即可。
于是可以对位置 (i) 设 (b_i) 表示操作 1 对位置 (i) 的贡献,(c_i) 表示操作 2 对位置 (i) 的贡献。如果 (b_i,c_i) 已经确定,那么接下来就只需要将 (b_i) 拆分成若干个不下降子序列,(c_i) 拆分成若干个不上升子序列,于是操作数 (=sum_{i=1}^{n}[b_i>b_{i+1}]+[c_i<c_{i+1}])。
对于这一问题就可以设 (dp) 状态 (f_{i,j}) 表示考虑了前 (i) 个位置,(b_i=j) 时的最小操作数,有:
因此 (f_{i,j}) 是随着 (j) 的增加单调不增的并且 (f_{i,0}le f_{i,a_i}+2)。因此我们只需要维护 (f_{i,j}=f_{i,a_i}+2,f_{i,a_i}+1,f_{i,a_i}) 的三个 (j) 的区间即可。
AGC040D Balance Beam
给定 (n) 条长度为 (1) 的边,A 通过第 (i) 条边的速度为 (dfrac{1}{a_i}),B 通过第 (i) 条边的速度为 (dfrac{1}{b_i})。
现在 A 可以对着 (n) 条边进行排序组成一条链,然后 A 从左端点出发,B 在链上随机一个实数点出发向右走,问 A 能追上 B 的概率最大是多少。(nle 10^5)。
view solution
对于这种位移固定的行程问题,考虑作出时间-位移图象(时间是纵坐标),那么 A 和 B 的行进路线就是从 ((0,0)) 出发,分别到 ((n,sum a_i)) 与 ((n,sum b_i)) 的两条折线。
接下来将 B 的图象向下平移直到再平移就不与 A 图象相交为止。若此时 B 图象与 (x) 轴交点为 ((p,0)),那么答案就是 (dfrac{p}{n})。问题转化为如何最大化 (p)。考虑从 ((p,0)) 出发,先沿 B 图象走,走到与 A 的交点时就改为走 A 图象,最终走到 ((n,sum a_i))。于是最大化 (p) 相当于要最大化从 ((p,0)) 到 ((n,sum a_i)) 的平均斜率。
枚举 (p) 所在的边 (x),那么如果将边 (i) 放在 (x) 后面,它至多贡献 (max(a_i,b_i)) 的斜率。于是将所有边按 (max(a_i,b_i)) 排序,选择一个后缀放在 (x) 后,可以发现一定可以通过将它们按 (a_i-b_i) 排序使得贡献全部取到 (max(a_i,b_i))。于是可以通过二分找到最小的后缀满足 (sum max(a_i,b_i)>sum a_i-b_x)。然后就可以算出 (p) 的大小更新答案了。复杂度为 (mathcal O(nlog n))。
CF1592F1/F2 Alice and Recoloring
view F1 solution
首先翻转包含 ((n,1)) 与 ((1,m)) 所在矩形的操作可以用两个包含矩形 ((1,1)) 的操作实现,因此我们可以忽略这两个操作。将矩阵进行二维差分,那么包含 ((1,1)) 矩形的操作等价于单点修改,包含 ((n,m)) 矩形的操作等价于同时修改 ((i,j),(n,j),(i,m),(n,m)) 的颜色。因为后一种操作本身会有 (4) 的代价,那么这个操作只会在这四个点颜色都是 (1) 的时候才会操作,并且只会执行一次,直接 (mathcal O(nm)) 遍历一遍判断是否有这样的 ((i,j)) 即可。
view F2 solution
唯一的区别是对右下角矩形的操作代价变成了 (2)。那么此时如果对点 ((i,j)) 进行操作,我们必须保证 ((i,j),(n,j),(i,m)) 都是 (1),因此每一行每一列最多只有一个点被选中作为操作矩形的左上端点。因此以行作为左部点,列作为右部点跑二分图匹配即可。
CF1495E Qingshan and Daniel
一个环上有 (n) 个人分属两个阵营,每个人手上有两张牌。一开始第一个人打出一张牌,随后它右侧最近的与它不同阵营且有牌的人会打出一张牌以响应它,随后它右侧的不同阵营的人会继续响应。。。当无人能出牌时游戏停止。问最终每人出了多少张牌?(nle 5 imes 10^6)。
view solution
考虑游戏结束一定是手牌较少的一方所有人都出完了牌,不妨假设手牌较少的是 (A) 阵营,另一个是 (B) 阵营,那么出牌的过程一定是 (ABABABABdots) 或者 (BABABABAdots),如果是后者,我们可以让第一个人先出一张牌,将响应它的人看作第一个人就转化成了第一种出牌过程。
在 (ABABABAB) 的出牌过程中,可以大胆猜测 (A) 阵营的出牌顺序与最终每人出的牌数量没有任何关系。
证明考虑交换 (A) 中相邻的两次出牌的顺序,不妨设它们是由 (A_1、A_2) 出的牌,由 (B_1、B_2) 响应。若 (B_1=B_2) 那么显然不会有影响;否则意味着 (B_1) 出了自己的最后一张牌,如果 (A_2) 也在 (B_1) 前面,那么 (B_1) 响应 (A_2),(B_2) 响应 (A_1),否则在 (B_1) 会一直保留着自己的最后一张牌给 (A_1),不会产生影响,由此得证。
因此,可以假设 (A) 阵营是从左到右依次出牌的,这样就只需要维护一个 (s) 表示目前还没有被响应的 (A) 阵营牌数量,然后扫一遍环即可。
CF1572F Stations
一条大街上有 (n) 座大楼,从左至右第 (i) 座大楼标号为 (i),有高度 (h_i) 与参数 (w_i)。楼 (i) 能看到 (j) 当且仅当 (jin [i,w_i]) 且 (forall kin[i+1,j],h_k<h_i)。初始对任意的 (i) 有 (h_i=0,w_i=i),现在先后执行 (q) 次操作,操作有两种:
- (1 x y):重建大楼 (x) 使它比所有其他大楼都高,且 (w_x=y)。
- (2 l r):令 (b_i) 表示能看到大楼 (i) 的大楼数量,求 (sum_{i=l}^{r}b_i)。
(n,qle 2 imes 10^5)。
view solution
直接维护 (b) 非常困难,考虑维护 (r_i) 表示从楼 (i) 能看到的最靠右的大楼,那么初始所有 (r_i=i),修改 (1 x y) 相当于单点修改 (r_x=y),并且对 (iin [1,x-1]) 令 (r_i=min(r_i,x-1))。
于是可以用 segment tree beats 维护 (r),每次只会将一些 (r) 相同的点的 (r) 同时减小到相同值,这可以通过依次对 (b) 的区间修改 (mathcal O(log n)) 维护,segment tree beats 会产生 (mathcal O((n+q)log n)) 次操作,于是总复杂度为 (mathcal O((n+q)log^2n))。
CF1534G A New Beginning
在一个二维直角坐标系上有 (n) 个点,第 (i) 个点为 ((x_i,y_i)) ,你初始在 ((0,0)),可以不花费任何代价向上或向右移动任意长度。现在你想要在移动过程中摧毁这 (n) 个点(顺序随意),在点 ((x,y)) 摧毁点 ((x_i,y_i)) 需要花费 (max(|x-x_i|,|y-y_i|)) 的能量,问最少少花费多少能量才能摧毁所有点。(nle 8 imes 10^5,0le x_i,y_ile 10^9)。
view solution
题目中的距离是切比雪夫距离,是一个难以处理的点,但是不难发现,一定存在一个方案使得对于任意一个点 (i) ,在路径上摧毁它的点在 (x+y=x_i+y_i) 上。
证明考虑设最终摧毁它花费的代价为 (t),那么一定是在以 ((x_i,y_i)) 为边界的一个边长为 (2t) 正方形的边界上进行的摧毁。
考虑移动过程中抵达这个正方形的位置,显然这个位置不可能是右边界或上边界。如果是下边界,那么下一步不可能是向上(这样向上一步再摧毁代价就变成 (t-1) 了),而只能一直向右到达右下端点,走到左边界同理只能一直向上到达左上端点。因此,一定存在方案是在这条对角线上完成的摧毁。
接下来考虑将每个点按 (x+y) 的大小排序分层,可以按照层进行 (dp),设 (dp_{i,j}) 表示走到第 (i) 层时 (x=j) 的最小代价,转移考虑枚举上一层的 (x) 坐标:
直接 (dp) 复杂度还是太高,由于每次转移新增的 (|x-x_i|) 是一个下凸的函数,那么将 (dp_{i,x}) 看作关于 (x) 的函数 (dp_i(x)),这个函数也是一个下凸函数。
接下来就可以使用经典技巧:用数据结构维护凸函数。在本题中,每次转移只会增加 (pm 1) 的斜率,因此可以直接维护 (dp) 函数的每个斜率增加 (1) 的位置,并维护斜率为 (0) 的区间。那么最优解一定在斜率为 (0) 处取到。
转移时,在 ([x-Delta,x]) 区间取 (min) 进行转移,相当于将斜率为 (0) 的部分向右拉长 (Delta) 的距离,并且斜率 (>0) 的函数同时向右平移 (Delta),于是我们直接维护目前向右移动的距离,并用两个堆分别维护斜率 (<0) 与 (>0) 的部分斜率的变化点。
加入 (|x-x_i|) 时,这会导致 (x_i) 左侧斜率 (-1),右侧斜率 (+1),因此需要在 (x_i) 处增加两个变化点,对于最低点的变化,则根据 (x_i) 于最低点的大小关系分三类讨论即可,具体参见代码。
CF1466H Finding satisfactory solutions
有 (n) 个人与 (n) 样物品,每个人选择了一样物品,同时第 (i) 个人对这 (n) 样物品有一个好感度的排行,按好感度从大到小一次为 ({s_{i,1},s_{i,2},dots,s_{i,n}})((iin [0,n]))。
如果存在这 (n) 个人的一个子集 (s) ,使得可以对 (s) 内的人的物品进行一次交换,使:
- 不存在任何一个人交换得到了一个更不喜欢的物品。
- 存在至少一个人得到了一个更喜欢的物品。
那么就认为当前物品的分配方案是不好的,否则是好的。
现在给出了一种物品分配方案 ({a_n}),问有多少组 ({{s_{1,n}},{s_{2,n}},dots,{s_{n,n}}}) 使得这一分配方案是好的。
(nle 40)。
view solution
考虑如果 (i) 比起 (a_i) 来更喜欢 (j),就从 (i) 向 (j) 连一条白边,再对每个 (i) 从 (i) 向 (a_i) 连一条黑边,那么 ({a_n}) 是好的意味着这张图不存在一个包含白边的环。
涉及到环,考虑缩点,那么环一定在某个强连通分量内产生。因此,如果某个强连通分量中包含白色边,那么一定可以在该强连通分量中找到一个包含白边的环,于是我们希望所有强连通分量都只有黑边构成。
注意到黑边是哪些是确定的,因此我们对这些黑边进行缩点,事实上最终的强连通分量就是目前缩点得到的强连通分量,白边只能在它们之间连接。当然,由于黑边中每个点的度数都是 (2) ,缩点其实得到的就是 ({a_n}) 排列中的环。问题转化为给定一些点,在它们之间连边得到一个 (DAG)。
当然由于本题是选排列而不是连边,因此某一个点连出 (j) 条边意味着排列有 (j!(n-1-j)!) 中选择方案。
考虑状压 (DP),设 (f_s) 表示对 (s) 内部的点组成 (DAG) 的方案。这是一个经典套路,可以枚举 (s) 中入度为 (0) 的点集 (T),计算 (T) 向 (S/T) 连边的方案数(记为 (g_{T,S/T})),从 (f_{S/T}) 转移过来。
但有可能 (S/T) 的部分中也有入度为 (0) 的点,需要容斥枚举实际上入度为 (0) 的点集 (P),有:
由于
于是
暴力枚举子集进行 (DP) 复杂度依然太高,注意到 (S) 中大小相同的环的贡献是一样的,因此可以将 (S) 改为所有不同长度的环的数量,这样一来状态数大大优化,据官方题解应该是 (mathcal O(P(n)n^3)) 的,其中 (P(n)) 是 (n) 的拆分数,可以通过此题。