CF做题记录(2025.10)
10.9
CF2144D Price Tags
题意
给定一个序列\(c\),令\(a_i = \lceil \frac{c_i}{x} \rceil\),\(x\)是任意大于\(1\)的正整数。
\(f(x) = \sum\limits^{n}_{i=1} a_i - ky\),其中\(k\)是\(a\)与\(c\)相比新增数字的数量
求\(f(x)\)的最大值
思路
设\(c\)中最大值为\(A\),首先容易发现\(x \in [1, A]\)
考虑当\(x\)固定时怎么做,
首先,计算出\(a\),\(O(n)\)
接着,可以用双指针统计出\(k\),\(O(n)\)
总复杂度\(O(nA)\),爆炸
考虑优化,计算过程显然是没有什么优化空间了,但也许我们并不需要计算出\(a\)数组
我们只需要知道,对于每一个值\(v\),\(c\)中是否存在除以\(x\)后是\(v\)的值即可
也就是说,是否存在\(c_i\), 使得\(v = \lceil \frac{c_i}{x} \rceil\)
那么,\(c_i \in [(v-1)x + 1,\ vx]\)
只需统计\(c\)数组在这段区域内的元素个数即可,用桶很好实现,时间复杂度\(O(\frac{A}{x})\)
对于统计过程,用桶也很好实现,按上述方法统计出\(v\)在\(a\)中的出现次数,减去\(v\)在\(c\)中的个数,与\(0\)取\(max\)就可以得到新增的元素了,时间复杂度\(O(\frac{A}{x})\)
总复杂度\(O(n + \sum\limits_{x=1}^{A} \frac{A}{x}) = O(n + A\text{log}A)\)(加上预处理桶的时间)
10.10
*CF2112D
题意
给定一棵\(n\)个节点无根树,试为每条边确定一个方向,得到一个有向图,使得图中满足存在一条从\(u\)到\(v\)的路径的点对\((u, v)\)的数量恰好等于\(n\)
思路
首先容易发现得到的有向图\(G = \{V, E\}\)是一个\(DAG\),那么如果我们确定了每条边的方向,就可以用拓扑排序统计出每个点可以到达的点数。
设\(f_u\)为点\(u\)在图\(G\)能够到达的除自身以外的点数,则\(f_u = \sum\limits_{(u, v) \in E} (f_v + 1)\)
那么,就可以求出整个图中满足条件的点对数\(y\)
其中,\(ind_v\)是节点\(v\)的入度
那么,根据条件,就有
观察式子,由于\(ind_v\),\(f_v\)均为非负整数,所以求和式中至多有一项为\(1\),其余均为\(0\)
因此,图中至多有一个节点的\(ind\),\(f\)均为\(1\)(即在原树中度数为\(2\)),其余点要么入度为\(0\),要么可以到达的点均为\(0\)(出度为\(0\))
所以,只需找到树中的\(2\)度点,从该点开始搜索,确定每条边的方向即可
10.11
*CF2148F
首先根据题意,我们只需要从第\(0\)项开始,每次选当前项及之后元素字典序最小的序列即可,分析可发现时间复杂度是\(O(\sum k \sqrt{\sum k})\)的
\(Proof:\)
首先发现这\(n\)个序列的长度中不同的数字个数是\(O(\sqrt{\sum k})\)
证明
设不同数字个数为\(t\),则
可以解得 \(t = O(\sqrt{\sum k})\)
那么显然,由于当我们选定了一个序列之后,指针就会跳到它的末尾,所以\(p\)只能取\(K = \{ k _i \}\)当中的值,这意味着我们至多会进行 \(O(|K|) = O(\sqrt{\sum k})\) 次选最小字典序序列的决策
而每次至多比较 \(O(\sum k)\) 个元素,故总时间复杂度是 \(O(\sum k \sqrt{ \sum k })\) 的
10.12
*CF2111E
字典序问题,考虑贪心。
考虑我们的目标是什么,因为只有\(a, b, c\)三种字符,所以我们想尽量让每个字符都变成\(a\),特别地,不能变成\(a\)的\(c\)也应尽量变成\(b\)
再具体一点,我们达成目标的途径有两种:
- 直接实现:\(x \rightarrow a \quad c \rightarrow b\)
- 间接实现:\(b \rightarrow c \rightarrow a \quad c \rightarrow b \rightarrow a\)
那么,我们就可以贪心地从左到右考虑每个字符,看其能否变得更小,并且,直接实现的优先级高于间接实现
这是因为如果同时有\(x \rightarrow a \quad x \rightarrow y \rightarrow a \ (x, y \in \{b, c\}, x \neq y)\),那么我们选择直接实现可以为后面的\(y\)节省下\(y \rightarrow a\)这次操作,更优
而且,由于简介实现对于操作的顺序有要求,所以我们每次选择最早的可行操作。
证明
首先证明直接实现的优先级高于间接实现
不妨设现在考虑将选择对字符\(b\)的操作方式,假设一个最优解选择了\(b \rightarrow c \rightarrow a\),且存在操作\(A\):\(\ b \rightarrow a\)
若\(A\)被分配给了后面的一个\(b_2\),则将对\(b\)和\(b_2\)的操作交换,得到的解还是最优解。
若\(A\)被分配给了后面的一个\(c\),则将\(b \rightarrow c \rightarrow a\)中的\(c \rightarrow a\)给后面的那个\(c\),得到的解仍是最优解
接着,证明可以选择最早的可行操作
10.14
*CF2112D
很有趣的一道题
题意
对于一颗有根树,每个节点可以被染成绿、蓝、黄三种颜色,我们定义满足下面条件的染色方案是“美丽的”:
-
根节点是绿色的
-
在绿点和蓝点构成的集合中,任意两点间路径上没有黄点
-
在绿点和黄点构成的集合中,任意两点间路径上没有蓝点
其中条件2,3很关键,也很容易理解错,其中我漏掉容易漏掉的是在这两个条件的约束下任意两个绿点之间的路径上仅有绿点
题目要求的是:恰有\(m\)种不同的“美丽的”染色方案的有根树的最小节点数
思路
原问题有点困难,我们先考虑已知一棵树,求“美丽的”染色方案数。
根据上述题意,容易发现以下性质,
若一个节点为蓝色或黄色,则其子树中所有结点一定与它同色,即这棵子树的染色方案是唯一的
所以,我们只需要考虑一个点染为绿色的方案即可
设节点\(u\)染为绿色,其子树的染色方案数为\(f(u)\),则
所以,原问题就是求\(f(1) = m\)的最小树的节点数。
那么,很自然的,由于\(f(u)\)是乘积的形式,我们想到将\(m\)进行因数分解,于是,你发现原问题转化成了更小的子问题
但是,由于节点度数不确定,我们无法确定将\(m\)分解为多少个数,不过仔细思考,其实这不要紧,
设\(f(u) = m\)的最小树大小为\(g(m)\)
设\(m = f(u) = (f(v_1) + 2) \times \prod_{fa_v = u,v \neq v_1} (f(v) + 2) = (f(v_1) + 1) \times A\),你可以为后面的连乘加上一个根节点\(u'\),则\(f(u') = A\),则\(siz_u = siz_{v_1} + (\ siz_{u'} - 1\ ) + 1 = siz_{v_1} + siz_{u'}\)
所以,有\(g(m) = g(i - 2) + g(\frac{m}{i}) \quad ( m = i\times A = (f(v_1) + 2) \times A)\),\(O(\sum)\)
在手玩了几个样例之后,我们又会猜到一个结论,若\(m\)是偶数,无解
我第一反应是用归纳法证明的,显然\(m = 2\)是无解的,而偶数无论是怎样转移,一定都会分解出至少一个偶数,由归纳法可知偶数都无解。
其实更简单的方法是观察式子的奇偶性,因为叶子的\(f = 1\),而加二不会改变奇偶性,所以\(f\)一定是奇数。
那么,我们就可以愉快的用\(dp\)切掉这题了,预处理所有\(m\)的答案,转移时枚举因数,时间复杂度\(O(m\sqrt{m})\),询问时\(O(1)\)回答。
还有一种更快的转移方法,类似筛法,枚举\(i\)的倍数,\(f(ki) = \min\{f(ki),\ f(k) + f(i - 2)\}\),\(O(\sum \frac{m}{i}) = O(m \lg m)\)
10.15
CF2043D
首先看到\(gcd\),我们想到把\(gcd\)除掉变为\([ \lceil \frac{l}{g} \rceil, \lfloor \frac{r}{g} \rfloor]\)中互质的数对的最大间隔
我们枚举这个差值,检验是否存在这样的数对即可。
假设答案是\(d\),则时间是\(O((r - l - d)^2 \lg A)\)的,\(A\)是值域(\(\lg A\)是\(gcd\)的复杂度)
但是,如果我们枚举到两个质数,查找就结束了,所以\(r - l - d \leq\)(\(l\) 到下一个质数的距离)\(+\)(\(r\) 到上一个质数的距离)
根据素数定理,\(N\)以内素数约有\(\frac{N}{\ln N}\)个,故两个素数的间隔约为\(O(\ln N)\),故\(O((r - l - d)^2 \lg A) \approx O(\ln^2A\lg A) = O(\lg^3 A)\)
10.17
Edu167
A
一开始结论猜错了,后来才改过来,问题的关键在于人与金币纵坐标之差,我们应尽可能让人往金币下方跑,然后等待金币落下所以就是\(y_i - (|x_i|-1) \geq - |x_i|\),即\(y_i \geq -1\)
B
思路一直在往匹配上想,对于每一个\(b\)中字符,找到在\(a\)中第一次出现位置,指针后移,继续匹配,但这并不一定是最优的,反例\(a=cbcbc \quad b = bbbcc \quad s_{min} = bcbcbcc\),问题在哪呢?我们发现问题在于在上述策略中,我们始终把\(a\)放在\(s\)的开头位置,但实际上\(a\)可能在\(s\)中间,前面是由\(b\)提供的,所以就应该枚举\(a\)的位置,即\(b\)的一段前缀,再进行匹配,还有,这种题数据范围比较小,可以先往暴力的思路想,因为可能大概率没有什么比较巧的高效算法
C
最优化问题,我们考虑贪心(实际上在这之前应该二分,dp,贪心都考虑一遍,觉得哪个比较可行,这里觉得是贪心的概率比较大),我们想到对每个人再\(a_i\),\(b_i\)中选最大值,对吗?因为如果\(a_i > b_i\)则\(b_i = 0\)或\(-1\),也就是对第二部电影没有贡献,或会使答案变得更劣,所以我们可以放心的选最大值。这就自然引出了其它情况,如果\(a_i=b_i\)会怎样? 如果是\(0\)显然不用考虑,否则,由于取最小值作为最终答案,我们可以最后再分配这部分观众,使两电影得分尽量平均
D
依旧是贪心,我们直觉上可以感觉到选消耗(铸造所用 \(-\) 融化返还)最少的最优,其次我们发现得分一定是两分两分地得,因为一旦铸造后就可以再融化,所以我们可以把铸造所用视为“触发条件”,消耗视为代价,得到策略:对每一堆选择\(\max\{a_i - b_i | a_i \leq c\}\),重复这一过程,那么,我们可以将物品按\(a_i\)排序,预处理出一段前缀中代价最小的物品,然后对于每个\(c_i\)不断选择最优的物品,扣除代价,不断重复直至无法找到可行的物品。
但是这样很慢,我们发现在前面所说的过程中,只要\(c_i\)固定,那么整个过程及最终收益就都固定了,并且这个过程是可以递推的,所以可以考虑将所有\(c_i\)的收益存其来,但\(c_i \leq 10^9\),如何是好,观察数据范围,我们发现\(a_i, b_i \leq 10^6\),是否可以存储\(10^6\)量级的数据呢?通过观察我们发现,其实\(c_i\)在经过一次循环后就会变到\(10^6\)以下,因为只要能够取一个物品,就会一直取,直到\(c_i < a_x\),于是这题就完美地解决了
事实上,一开始做题时我们就应该注意到值域上的限制,数据在\(10^6\)以内,可能暗示我们可以储存一些基于值域的信息,值域在\(10^9\)级别,可能不太有希望存储,也可能暗示我们进行离散化,或是其他一些转化。
10.18
Edu166
C
赛时没想明白就开始写了,浪费了大量时间,最直观的想法是我们可以先求出不考虑最后一个人的结果,然后再枚举每一个人,在\(O(1)\)时间内调整答案。
记不考虑最后一个人时第\(i\)个人的职位为\(job_i\)
假设第\(i\)个人缺席,设它在原来的结果中的职位是\(x\),那么它前面的人不会受到影响,那么对于后面的人来说,现在多出了一个\(x\)类型的职位,那么我们可以找到后面第一个分到\(x\)最优,但并没有分到职位\(x\)的人\(j\),然后给他职位\(x\)。
那么,对于\(j\)之后的人,又空出来一个类型为\(y \neq x\)的职位,就可以递推这个过程。。。
实际上并没有这么困难,因为到\(j\)的时候职位\(x\)已经没了,所以后面不会有分到\(y\)最优,但分到\(x\)的人,除了第\((n+m+1)\)个人,因为它一开始没被考虑,所以,我们只需要考虑第\(j\)个人和第\((n + m + 1)\)个人即可
如果不存在这样的\(j\),那么空出来这个位置给\(n+m+1\)就行了
所以,再仔细考虑一下,实际上我们只需要考虑第一个没被分到最优职位的人\(a\)就行了
如果\(i < a\)且\(job_i \neq job_a\),就让\(a\)继承\(i\)的职位,\(n+m+1\)继承\(a\)的职位
否则,就让\(n + m + 1\)继承\(i\)的职位即可
应该想得差不多了在开始写,还要充分挖掘条件,赛时没有意识到\(a\)之后的人职位都相同,没想到简单的做法,而且没想到递推就开始写了,干了很久
D
括号序问题,根据条件列方程即可
设一个前缀\([1, i]\)中\(1\)的个数与\(0\)的个数之差为\(f_i\)
设满足条件的区间为\([l, r]\),则
考虑枚举每一个\(l\),计算可行的\(r\)的个数,对于第一个条件,可用线段树查找\([l, n]\)第一个\(f_i > 2f_{l-1}\)的位置,对于第二个条件,由于\(f \in [0, \frac{n}{2}]\),值域较小,可以直接维护每一个值的出现位置,再其上二分即可
E
赛时没时间做了。。。(C拖了太久)
读完题目,观察完样例后,你会发现输入中给出的数最后会形成一种类似BST的结构,\(l_i\)一定位于\(r_i\)之前,而且其中的较大值是分割前这一大段的最大值,所以新分成的两小段就会“继承”原来大段在整个序列中的位置关系,因此如果我们建立一颗BST,若\(r_i\)已在树中,将\(l_i\)作为它的左儿子,反之亦然,最后中序遍历即可得到给出的数的顺序。
当然,直观来看,由于每个数对\((l_i, r_i)\)相当于在一个阶段的某个数(\(l_i\)或\(r_i\))前/后新插入一个数,这个过程也可用链表来做,我当时只想到BST,没想到链表
于是,输入中给出的数的相对位置就确定了,且每个数都是自己段里的最大值,这样,我们就把问题转化成这样:
有若干个分界点(序列头、尾,即第\(0\)、\(n+1\)项可看作两个值为\(0\)的分界点),两个分界点之间的数应该比左、右分界点的最大值小,求合法的序列个数
对于这个问题,我们可以从插入的角度来看,从小到大考虑每一个未出现的数,考虑将其插入到某两个相邻分界点之间,看能插入到几个空中。但是这有一个问题,先插入的数会影响后面的数的可插入位置,那么我们可以改变插入顺序,从大到小插入,因为如果一个数不能被插入到某个空中,比它大的数也一定不能,这样,我们记录可以插入的空位,累乘到答案中,就可以了
为什么想到BST/链表?
从这个题目来说,我们实际上是要维护给出的数的相对位置,这是一种有序状态,可以通过维护前驱来实现,而BST和链表本质上来说就是维护一个数的前驱和后继的数据结构,链表可以看作是BST退化成一条链的结果,这也是为什么需要平衡树来提升效率,普通的BST容易退化成与链表一样的\(O(n)\)复杂度。并且,本题中给出了每个元素在插入时刻的后继,可以直接插入,不需查找。 在本题中,我们可以记录每个点在数据结构中对应的节点编号,从而快速查找
插入这一思路从何而来,转换顺序又是如何想到的?
排列组合问题中,插板法,插空法是常见的解题思路,可以往这方面思考。转换顺序则是在我们发现前面的数据会影响后面的计算的一种调整与尝试,我理解的是在遇到有后效性的计算过程时可以考虑调换顺序
10.20
Edu 165
C
赛时一直在想贪心QwQ
我的思路:对每个位置求出\(f_i = min(a_{i-1}, a_{i+1}) - a_i\),即每个位置替换后最多可以将总和减少多少,然后每次选\(f_i\)最小的位置进行替换,并更新两边的\(f\)值。但是有反例:\([1, 2, 5],\ k=2\)
我的策略:\([1, 2, 5] \rightarrow [1, 2, 2] \rightarrow [1, 1, 2]\)
最优解:\([1, 2, 5] \rightarrow [1, 1, 5] \rightarrow [1, 1, 1]\)
我们发现这种递增的序列按递增顺序来操作会更优,于是考虑反悔,将一个数删除后与其旁边替换它的数“并起来”,记一个 并起来的数的个数 作为权值,下次对这个数修改时就将这两个数一起更改。
但是这种思路好像也是错的T_T,因为当有两个数的\(f\)与权值均相等时,贪心就会随机选择一个,但是这两个数对于两边的\(f\)的贡献可能是不一样的,反悔并不能从根本上解决这一点
事实上,当你觉得需要反悔的时候,就应该顺带着考虑dp了,我们先从状态开始,紧贴题目,我们设\(f(i, j)\)表示对前\(i\)个数进行\(j\)次操作所能得到的最小和
但是转移似乎并不好实现,肯定是
的形式,\(g\)是对\([i - d + 1, i]\)操作\(k\)次能得到的最小和
思路卡住了,于是我们再回到具体的例子,还记得将贪心证伪的那个反例吗?我们再来看看
通过观察那个例子,我们容易得到一个结论:将一个长为\(l\)递增序列中的数全部变为其中的最小值至少需要\(l - 1\)次操作
仔细一想,实际上不需要递增也成立,但是如果有多个最小值呢?如\([3, 4, 2, 1, 1, 1, 2, 3]\)
其实它可以被分成\(3\)个最小值唯一的序列!\([3, 4, 2, 1]\),\([1]\),\([1, 2, 3]\)
那么我们是否可以用这个转移呢?我们可以假定为达到最小和,区间\([l, r]\)一定需要至少\(r - l\)次操作,这样,就有
尽管\(d - 1\)可能并不是最小的操作次数,但是由于这个区间可被分解,当前状态一定会被分解后的最优解更新,所以这样转移没问题。
也可以这样考虑,我们在转移过程中加入一个判断,当且仅当将\([l, r]\)转为最小和的操作次数等于\(r - l\)时,才进行转移,但是,当次数小于\(r - l\)时,一定会有比这个转移更优的决策点,所以删去这个判断也不会有影响
所以这个题就用\(O(nk^2)\)的dp解决就行了
这个题dp的转移很Educational,不知道能不能在别的题里应用
D
考虑当Alice已经选完时,Bob的决策,显然Bob会取其中\(b_i\)前\(k\)大的商品免费购买。
所以利润就是
我们要选择一个合适的集合\(T\),是的这个值最大
那么我们可以进行这样一步转化,钦定\(T\)中的前\(k\)大,找出使利润最大的其他元素(即其他元素中所有是正数的\((b_i - a_i)\)的和)
于是我们考虑将物品按\(b_i\)从大到小排序,我的思路是枚举其中长为\(k\)的连续段,答案就是它后面元素的最大元素和减去该连续段中\(a_i\)的和
但是,这是最优的吗?我们需要检验,答案显然分为两部分,后面元素的最大元素和 与 该连续段中\(a_i\)的和,前者显然是后\((b_i - a_i)\)的所有正数和,但后者... 并不一定是最优的!你怎么保证这个连续段中\(a_i\)的和是最小的?
所以,正确做法应该是枚举一个断点,取前面\(a_i\)前\(k\)小的元素,后面所有\((b_i - a_i)\)正数的和作为该断点的答案
做题一定要检验正确性啊!赛时一直傻傻地以为选连续段是最优的。。。
记住,OI也需要证明!一定要确定自己的策略的正确性!
Edu 164
D
首先我们发现,一个集合的value就是将其中的数分为两组,每组和的最大值的最小值,也可表述为使用集合中元素能凑出的大于总和一半的值的最小值。
这个问题我们可以用背包解,然后......指数复杂度!爆炸!
然后我就罚坐了\(1h\)....
这个时候我们应该发现对每个子集用背包求解上述问题是没有优化前途的,所以应该考虑猜结论,事实上,设集合中元素之和为\(s\),若存在一个元素\(a > \frac{s}{2}\),则答案就是\(a\),否则,答案是\(\lceil \frac{s}{2} \rceil\)
如果我们可以把这些小球分成长为 l 的两段,并且每一段的同个位置上的小球颜色都不相同,那就取到了答案下界。考虑我们把每种颜色按照球数从多到少地往段里面塞,塞完第一段塞第二段。那么违背要求当且仅当某种颜色在第一段末尾没能塞完,塞到第二段开头结果重叠了,然而此时这个颜色的出现次数一定 \(>\frac{s}{2}\),说明存在主元素,不符合预设条件。
from 洛谷题解
有了这个结论,这题就迎刃而解了,我们只需要用背包算出能得到的每个和的方案数,顺便在计算时根据当前枚举的元素与和的一半的大小关系,更新答案即可。
还有,数组\(a\)应从小到大排序,防止dp转移时已经凑出部分的和含有比所凑总和的一半大的元素。
Edu 163
D
赛时一直在考虑使用字符串相关算法,实际上枚举就行了,做了\(1h\)才把思路转换过来
10.21
Edu 162
打得很废的一场
A
上来先读错题。。。
然后就开始猜结论,猜到了,赢
还是来分析一下吧,我们考虑问题中的不变量:\(1\)的个数,操作过程只是让\(1\)变得更紧凑了,而没有改变它的个数。
接着,我们考虑变化量---紧凑程度,那么如何描述这个量呢,一种简单的方法是用最右边与最左边的\(1\)的下标之差来描述,设左右边界分别为\(l, r\),则“紧凑程度”\(f = r - l\),那么,当操作结束后,有\(f = c - 1\),\(c\)是\(1\)的个数
那么,考虑我们操作对\(f\)的影响,容易发现只有当我们操作最右边的\(1\)时,\(r\)会减小\(1\),\(f\)也随之减小\(1\),其他情况均会使\(f\)不变或增大
所以答案就是\(\Delta f = r - l - (c - 1)\)
找到不变量 \(\rightarrow\) 找到并刻画(用式子表示)变量 \(\rightarrow\) 在变量与不变量之间建立联系 \(\rightarrow\) 考虑操作对变量的影响
这种思路也许有助于猜结论和分析问题,也能帮助我们证明结论的正确性。
当然,在这之前,瞪眼法与观察样例永远是更加使用的方法,对于简单题,直接观察通常更为高效
B
很久没猜到结论,最后完全是靠运气撞出来的,失败
我的猜结论历程大概是这样的:
-
考虑单个怪,假设每秒都对其发射\(k\)发子弹,则最少需要\(\lceil a_i / k \rceil\)秒,所以判断\(\lceil a_i / k \rceil \leq x_i\),但是这种判断方式的整体性不强,仅仅关注个体,显然无法得到正确答案
-
我们加入更多整体信息,由于我们要击杀所有怪,所以打出的子弹的总和一定等于所有怪的生命值之和,那么我们考虑能否在给定的时间内打完这些子弹,即判断\(\sum a_i \leq kx_{max}\)
-
过了半个小时之后,我才灵光一闪,猜到了正确结论,赛后想来有理有据的分析应该是这样的,猜想2的缺点与猜想1相反,整体性过强,对个体的关注过少,直观的想法是,可能有一个生命值很大的怪,但到角色的距离很近,这样尽管后面的怪生命较大,能够平摊这个大生命值,但角色还是会被击杀,所以,我们想到(不知道怎么想到)枚举到角色的距离,并进行2中的检查,就可以了。
这题的思路我也不是很懂,大概有以下几点,
-
可以先随便猜一个结论,然后举反例并分析其问题,是过于关注个体还是过于关注整体?如果过于关注部分,可以尝试考虑各部分的和或并;如果过于关注整体,可以尝试对多个部分进行同样的策略(云里雾里qwq)
-
要有一些第一直觉,比如直觉上觉得距离近,生命值大的怪优先级更高,并将其与自己的策略相结合
C
调整法
首先,我们初始让\(b=a\),则第一个条件显然满足,接下来考虑对\(b\)中的每个元素进行一些加减,使得\(b\)与\(a\)相同位置上的数不同
那很显然,如果我们有偶数个元素,只需让一般的元素\(+1\),另一半\(-1\)即可,奇数的话就是\(\lceil s / 2 \rceil\)个元素\(-1\),\(\lfloor s / 2 \rfloor - 1\)个元素\(+1\),还有一个元素\(+2\)(\(s\)是序列长度)
但是,由于\(\forall b_i > 0\),\(b_i = a_i = 1\)的位置是不可以减小的,只能增加
那么,我们考虑按长度奇偶性分类,设长度为\(s\),\(1\)的个数为\(c\),\(a\)序列总和为\(s\):
若\(s\)是偶数,则
如果有至少\(s/2\)个位置可减,输出YES
否则,说明\(1\)的个数大于\(s/2\),则将每个一\(+1\),总和增大\(c\),其余的数总共就要减去\(c\),由于每个数至多减到\(1\),所以可供减掉的值为\((sum - s)\),若\(sum - s \geq c\),输出YES,否则输出NO,并且,由于\(1\)的个数大于\(s/2\),根据鸽巢原理,剩下的每个数都至少会被减一,从而与原序列不同
长度为奇数时同理,这里不再赘述
D
最优化:dp,贪心,二分,枚举
我们选择二分,考虑check怎么写
即,判断一个点是否能在时间\(t\)内被吃掉
首先,我们发现,能在时间\(t\)内吃掉\(i\)的slime一定在区间\([i + 1, i + t]\)或\([i - t, i - 1]\)内,我们先考虑右半边区间,那么我们可以考虑其中的最大值(极端原理),显然,最大值能够吃掉这个区间内的所有slimes,最后再来吃\(i\),于是,我们只需要判断\(sum[i + 1, i + t] > a_i\)即可。
但是,还有一种特殊情况,由于本题的条件是严格大于时才能吃掉,所以如果[i + 1, i + t]中全都是一个值,就不能吃任何slime,所以,我们在二分的时候特判下\(i + 1\)与\(i + t\)之间的元素是否全相等,如果是,则令\(l = mid + 1\)
左半边区间同理,两者都求完后取\(\min\)即可
tag: 极端原理,最优化
10.22
Edu 161
C
记住预处理的重要性!
D
暴力是非常好想的,每轮遍历整个序列,找出会被击杀的怪,将其删去,不断重复这个过程
思考瓶颈在哪里,暴力有两个部分,查找和删除,删除可用链表实现,那瓶颈就在于查找
于是,查找=数据结构=线段树!尽管线段树可能不是必要的,但确实有用。。。
于是,我们就可以维护一个序列\(f_i = a_{i-1} + a_{i+1} - d_i\),查找其中大于\(0\)的位置,找到所有这样的位置后,再将它们删去(与链表结合),并更新两边位置的\(f\)值
这样的话,每轮的查找次数应该等于该轮被击杀的怪的数量\(+1\),更新次数应该是前面这个数量的\(2\)倍,由于每个怪只会被击杀一次,总的复杂度就是\(O(n\lg n)\)的
虽说是偏解,但好歹做出来了。。。
其实,正解的思路异曲同工,同样是发现了每次删除只会更新有限个元素,均摊后复杂度较优的特点。
我们发现每次删去一个元素只会对相邻的两个元素产生影响,于是就可以记录下每次删去的元素,只更新可能被更新的元素,即删去的元素的相邻元素,就可以做到均摊\(O(n)\)的复杂度。
本题的关键点:每个元素只能被删去一次,且每次更新\(O(1)\)个元素,均摊后复杂度较优
E
从特殊情况入手,一个长为\(n\)的单调递增序列的上升子序列数是多少?显然是\(2^n\),然后考虑在它后面加一些数,假设加一个\(x\),则对于前面小于\(x\)的部分,在其末尾加上一个\(x\)又能得到\(2^y\)个单增序列(\(y\)是前面序列中小于\(x\)的数的个数),再继续加也同理
好吧,到这里思路就已经很明显了,每次加一个数的贡献都是\(2\)的幂次,所以就将输入的数二进制拆分,根据其为\(1\)的位进行构造即可
1060 div.2
B
先对题目有些整体感受:
-
整个序列类似一种波浪型,偶数位在波峰,奇数位在波谷
-
操作一代价为\(0\)
结合一和二,我们发现,应该多用操作一,且应该让偶数位尽可能大,于是,我们先对偶数位都进行一遍操作一,因为操作二只会让操作一中的最大值变得更小,接着我们对计数位进行操作二,计算其降到\(\min(a_{i-1}, a_{i+1}) - 1\)及以下的最小代价就可以了
C1
这题研究数值性质,显然存储一些值域上的信息是会比较有利的
说到不互质的数,最常见的就是所有偶数了,所以,如果序列中出现两个偶数,就不用操作,如果有一个偶数,一个奇数,则给那个奇数\(+1\)即可,若有两个奇数,分别\(+1\)即可,所以,我们发现答案不超过\(2\)。
因此,对于每个质数,我们用\(c_x\)维护有多少个数被它整除,如果存在一个位置大于一,输出\(0\)
接着,考虑\(1\)次操作的情况
如果有一个偶数,输出\(1\)。
枚举\(a_i\),将\(a_i + 1\)分解质因数,如果其中一个质因数的\(c\)不为一,输出\(1\)
否则,输出\(2\)
完结撒花
C2
与C1的区别在于修改的代价不再为\(1\)
我们发现C1中的结论仍然成立,最多仍然只需要\(2\)次操作就能让数列存在不互质的数,但根据样例,我们发现这似乎不是最优的。
显然判断操作为\(0\)的做法与C1一样,判断\(1\)次操作也可以套用C1中的做法,加个答案取 \(\min\) 就行了
关键在于操作次数为\(2\)的方案,一个重要的观察是,\(ans \leq b_{1st} + b_{2nd}\),其中 \(b_{1st},\ b_{2nd}\) 分别是 \(b\) 中的最小值和次小值,因为我们只需要随机选两个奇数即可,所以不妨选代价最小和次小的(如果最小的或次小的之一对应的 \(a\) 值是偶数,那么在检查一次操作时会有更小的答案,这个界仍然成立)
这时我们再考虑多次操作的问题,根据上面的结论,\(ans \leq b_{1st} + b_{2nd}\),我们发现只有\(b_i\)最小的元素可以加多次,不然一定会超出这个界,为了出现不互质的数对,增加后的数一定是能被 能整除其他数的质数之一(已预处理) 整除所以只需枚举质数,计算这个数模该质数的余数,算出最小代价即可。
10.23
Edu159
B
注意分讨清楚
C
假设最终得到的数是 \(t\),则 \(x\mid t - a_i\),所以 \(\forall a_i \equiv k \pmod x\),也就是 \(\forall a_i, a_j,\ x \mid a_i - a_j\)
那么,我们将数组从小到大排序,求出相邻两项之差的 \(\gcd\) 作为 \(x\),然后查找小于最大值的最大的未出现的模 \(x\) 与数组中的数同余的数,即可
具体来说,查找时看哪个差值大于 \(x\) 就可以了。
还有一种特殊情况,如果原数组除以 \(x\) 得到的整数是连续的,我们既可以选 \(a_{\max} + x\),也可以选 \(a_{\min} - x\),两者得到的操作次数是一样的
D
对操作对坐标的影响求前缀和(如 \(\text{R} \rightarrow x \leftarrow x +1,y \leftarrow y\))
那我们只需要在数组中快速查找是否存在某个坐标就行了,反转可以通过某些前缀和推导进行转化
于是现在问题转化成这样:如何在 \([l, r]\) 内快速判断某个数是否存在
哈希表!
当然,为了防止冲突,也可以使用 STL 中的 map,复杂度会多一个 \(\log\)
但是,可能会有多个位置有同样的值,而由于 map 只能将一个关键字映射到一个值上,这可能会导致划分区间后我们找不到答案
然后卡了\(1h\)...
最后想出一种复杂的解决方法:将每个位置上的 \(pair\) 离散化,然后用 \(vector\) 记录每个 \(pair\) 出现的所有位置
你说的对,但实际上只要用 \(map\) 将每个 \(pair\) 映射到一个 \(vector\) 就行了
ZWR大神的做法:
反转开两份前缀和跟 \(map\) 还是太复杂了,实际上中间翻转的部分可以看作将原路径在平面上关于起终点连线中点中心对称后得到的,但我们并不需要实际对这段路经进行对称,只需要将查询的那个点对称过去就行了,即如果原路径经过了点 \(P'\),那么反转后的路径就一定经过点 \(P\),\(P\) 与 \(P'\) 关于对称中心对称
stO ZZFLS_zwr Orz
E
分析题意,\(C(a, b)\) 就是 $l_a + l_b - $ 将 \(a\) 反转后与 \(b\) 的最长公共前缀长度,但是由于我们要批量求这个东西,所以我们考虑 \(Trie\),将所有串反转后的串插入到 \(Trie\) 中,然后用每个原串在 \(Trie\) 中匹配,统计答案即可
Edu 158
C
\(40min\) 时才过
条件:下取整,对整个数列进行操作,每轮操作的 \(x\) 固定
目标:使数列中数字全相同
除法,且对整个数列操作,意味着整个数列中数的相对大小关系是不变的
所以,数列中数字全相同 \(\Leftrightarrow\) 数列中的最小值等于最大值
那么我们就定义 \(f = a_{\max} - a_{\min}\),目标就是让 \(f\) 变成 \(0\)。
取整,考虑取整的性质,\(\lceil \frac{a+x}{2} \rceil = \lceil \frac{a + x \bmod 2}{2} \rceil + \lfloor \frac{x}{2} \rfloor\)
因为同时给所有数加上 \(\lfloor \frac{x}{2} \rfloor\) 不会使 \(f\) 变小,所以我们发现有用的实际上是 \(x \bmod 2\),即,\(x\) 可以只取 \(0\) 和 \(1\)
然后,我们考虑数列的最大值 \(a\) 和最小值 \(b\),\(a > b\),若 \(b\) 是偶数,则如果我们取 \(x=1\)
那么,
若 \(x=0\)
故当 \(b\) 是偶数时,应取 \(0\),同理,当 \(b\) 是奇数时,应取 \(1\)
D
我的思路:
首先猜测第一个选最大值最优,然后考虑怎样情况是最差的,显然是每次都选边界上最小的击杀,然后就做完啦!!!
Wrong Answer on test 6
好吧,反思过后我们发现第一个结论并不一定成立,即第一个不一定要选最大值
6
1 5 6 3 4 1
在这个例子中,选 \(6\) 的最坏情况是 \(9\),选 \(5\) 的最坏情况是 \(8\)
看来我们必须要考虑所有初始点了。。。 \(O(n^2)\)
现在我们有两条优化思路:
- 尝试优化枚举初始点的过程(利用排除法)
- 尝试优化对一个点计算答案的过程
思考良久,好像这两条都没什么前途。。。
最后,看完题解,发现第二条结论也是错的?!
73
98 100 99 1 1 1 1 1 1 1 1 (repeat 70 times)
在这个例子中,取完 \(100\) 取 \(98\) 并不是最坏情况,取完 \(100\) 然后一直取右边的,最后再取 \(98\) 显然比结论中的策略更差
那么我们只能转而考虑其它方法,最后得出对于一个中心点,它左边的点的最晚击杀时间应该是它右边的点的个数,即 \(n - i\),中心点右边的点则是它左边点的个数,即 \(i - 1\)
所以,一个中心点的答案应该等于每个点的生命值 \(+\) 它的最晚击杀时间,然后做完了。。。
从这题中我们可以看到,猜结论是要的,但是它不能解决所有问题,当我们发现没有优化思路或无法再进行下去时,可以考虑结论是否正确,选择修正结论,或摒弃结论,换一个思路继续思考
10.24
Edu 155
B
一个结论是,如果要满足每个方格都有与它同行或同列的 chip,要么每行都有一个 chip,要么每列都有一个 chip。
因为如果存在一列和一行没有 chip,那么这一行与这一列的交叉点一定不满足题意
所以,答案就是 \(\min\{\sum_{i=1}^{n} (A_i + B_{\min}),\ \sum_{i=1}^{n}(B_i + A_{\min})\}\)
C
计数题
因为这是一个 \(01\) 串,所以相邻字符不同就是要变成 \(010101....\) 或 \(101010...\) 的样子,那么我们只需要从每个连续相同数字段中删去元素,直到每个段都为一即可
选择所删去元素的方案数为 \(\sum \binom{c_i}{c_i - 1}\),由于操作顺序可变,应该再乘上操作次数的阶乘
D
异或 \(\rightarrow\) 可差分 \(\rightarrow\) \(f(l, r) = s_r \oplus s_{l-1}\)
然后没有思路,因为异或对于加法,乘法均没有分配律
换个思路,位运算 \(\rightarrow\) 每位独立,分开处理,done
所以我们只需要维护每一位上,区间 \([l, r]\) 间一的个数是奇还是偶,如果是奇,这一位就会为 \(f(l, r)\) 贡献 \(2^b\),\(b\)是当前考虑的位,所以只需要分别记录特定奇偶性位置 后缀下标和 以及 数量 即可
E -- My First Interactive Problem in Contest !
题意:
给你一颗 \(n\) 个节点的树,你需要给树上的 \(n-1\) 条边染上颜色,然后与交互库玩一个游戏
游戏内容是这样的:
交互库会钦定一个不是根的点,你可以从这个点向周围移动,目标是用 \(d\) 步走到根(\(d\) 是结点的深度),即每一步都要向根的方向走。每次你到达一个节点(包括初始节点),交互库都会给出一个数组 \(c\),\(c_i\) 表示与当前结点相连的边中颜色为 \(i\) 的有多少条,然后你可以从中选择一种颜色,表示沿该种颜色的边行走,如果你选的颜色有多条边,交互库将随机选择一条
请你给出一种染色方案,使得颜色总数最小,并根据交互库给出的信息,每次选择合适的颜色,到达根节点
根据题意,我们每次都必须向根的方向行走,即,我们只能走从该节点连向它父亲的那条边,于是问题就变成,怎样染色可以使得这条边能被区分出来。
显然,\((x, p_x)\) 这条边的颜色必须与其他连向儿子的边的颜色不同,不然交互库不能保证一定走这条边
我们尝试手玩,根据深度进行染色似乎是一个比较合理的想法,我们发现,用 \(3\) 种颜色一定能够满足上述要求
具体的,第一层的边染成颜色 \(a\),第二层染成 \(b\),第三层染成 \(c\),第四层再染成 \(a\)...
即
所以,接下来,我们应该逐个分析,什么时候用一种颜色,什么时候用两种,什么时候用三种
首先,如果树只有两层,即节点一连向其他所有结点,那么答案是 \(1\)
接着,我们考虑为什么当时手玩的时候选择 \(3\) 种颜色,而不是 \(2\) 种
问题出在二度点上
我们在前面说过,染色的目标是将连向父亲的边区分出来,但是,对于二度点,即使两条边被染成不同颜色,我们也无法区分,因为两种颜色的边数都是 \(1\),解决的办法是规定一个顺序,例如在 \(3\) 种颜色的条件下,我们规定了 \(a\) 优于 \(b\),\(b\) 优于 \(c\),\(c\) 优于 \(a\),也就是说,如果 \(a\),\(c\) 同时为\(1\),那么我们选 \(c\)
那么,仿照同样的思路,如果想仅用两种颜色进行染色,就必须规定遇到二度点时两者的顺序,且这个顺序对于所有的二度点均相同,这里假设是 \(a > b\),即对于二度点,连向父亲的边染成 \(a\),连向儿子的染成 \(b\)
接下来我们就要看什么时候这个方案能够自洽
我们的方案:
- 一个点连向其父亲的边的颜色 \(\in \{a, b\}\) 且与其连向儿子的边的颜色均不同(这暗示着连向儿子的边颜色均相同)
- 对于二度点,连向父亲的边颜色为 \(a\),连向儿子的颜色为 \(b\)
首先,如果有一个二度点,方案满足,进一步的,如果所有二度点的深度均相同,方案满足
如果有两个二度点位于相邻层,方案不满足
我们进一步一般化,考虑两个二度点 \(x, y\) 和他们的 \(LCA\),\(d\),
容易发现,路径 \(x \leadsto d\) 和 \(y \leadsto d\) 上的边的颜色都是交替的,那么如果 \(\delta(x, d)\) 是奇数,\(d \rightarrow x\) 所在子树的边颜色一定是 \(a\),否则一定是 \(b\),\(y\) 同理,而由于从点 \(d\) 出发向下的边颜色均相同,所以,\(\delta(x, d)\) 与 \(\delta(y, d)\) 的奇偶性相同,即 \(dep_x \equiv dep_y \pmod 2\),也就是说,所有二度点的深度的奇偶性必须相同
但是通过手玩后,我们发现,根节点比较特殊,因为它没有指向父亲的边,所以边 \((1, u)\) 的颜色不必相同
这也好办,把策略改成,在每颗以根结点的儿子为根的子树中,所有二度点的深度的奇偶性必须相同,即可
于是,我们就可以判断出能否用两种颜色染色了
染色时需要注意,不能在判断的时候染色,应该在判断结束后,遍历根的每个儿子,如果在这个儿子 \(v\) 的子树中,所有二度点的深度都是偶数,那么 \((1, v)\) 染为颜色 \(b\),否则染为颜色 \(a\)
交互的时候,对于一种颜色的,只需要输出 \(1\) 即可,对于两种颜色,哪个的出现次数为 \(1\) 输出哪个,对于三种颜色,如果 \(c\) 中仅有一个位置不为 \(0\)(那么一定为 \(1\),叶子),输出这个颜色,否则按照顺序,同时有 \(a,b\),输出\(1\);同时有 \(b, c\),输出 \(2\);同时有 \(a, c\),输出 \(3\)(颜色的数字编号与字母编号数对应的,上文为了与节点区分,使用字母,实际写代码时应用数字)
10.25
Edu 154
C
注意题目中的操作:向序列末尾添加一个数,删除序列的最后一个数,这很符合栈的特点,于是我们考虑用栈解决这个问题。
具体的,栈顶表示当前序列的有序状态,\(0\) 表示无序,\(1\) 表示有序,\(-1\) 表示不确定
那么,栈的最低端两个元素(对应序列长度为 \(0, 1\) 的情况)应该为 \(1\)
对于 \(+\) 操作,如果栈顶为 \(0\),应继续入栈 \(0\),否则入栈 \(-1\) (在无序的序列后添加元素仍是无序的)
对于 \(-\) 操作,如果栈顶为 \(1\),应该将栈顶弹出,并将新栈顶改为 \(1\) (在有序的序列后删除元素仍是有序的)
也就是说,最后栈的结构应该是这样的:前面是 \(1\) 和 \(-1\),后面是连续的 \(0\),一直到栈顶
对于明确顺序状态的操作,如果栈顶与输入的数据不符且栈顶不是 \(-1\),出现矛盾,输出 NO
最后,如果没有输出 NO,输出 YES
D
容易发现,进行一次乘法后,如果所乘的 \(x > 0\),操作区间内的元素相对顺序是不变的,否则,元素相对顺序就会全部反过来
那么如果我们有一个单谷的序列,我们就可以通过对递减的那段前缀乘上一个负数,花费一次操作,完成排序
那么我们的问题就变成如何用最少次数将原序列变成一个单谷序列
对于最低点右侧的部分,它是递增的,则对于每一个 \(a_{i-1} \geq a_i\) 的位置,我们都至少需要一次操作将后缀 \([i, n]\) 乘上一个数(因为乘法只能让数变大,所以取后缀不劣),即我们只需统计一段后缀中 \(a_{i-1} \geq a_i\) 的位置的数量
对于最低点左侧的部分,同理我们可以得出应该统计 \(a_i \leq a_{i+1}\) 的位置
于是我们只需枚举最低点,将两边满足条件的位置个数加起来,再加上 \(\times (-1)\) 那一次操作(最低点下标为 \(1\) 不用乘),取最小值即可
E
好题!
首先,如果给定一个排列,那我们可以贪心地算出其中不相交子排列的个数,即每次都取最前面的子排列
求每个排列的 cost 和,我们考虑用贡献法,
我的想法,求出有多少个排列的 cost 值为 \(x\),最后加起来
真正的贡献法:求出每个子排列会被计算多少次
即,我们记 \(f_i\) 为只考虑前 \(i\) 个元素,以第 \(i\) 个元素结尾的排列被计算的个数
那么,利用容斥计算,先令 \(f_i = k! \times k^{i-k}\),即最后 \(k\) 个元素是一个排列,前面随便填的方案数,
然后,因为要满足贪心性质,那么 \(\forall j \in [i-k+1, i-1]\),以 \(j\) 结尾的排列不能被计算贡献,它被计算贡献的方案数是 \(f_j \times (i-j)!\),乘 \((i−j)!\) 是由于 \([i−k+1,i]\) 这个段中 \(1\) 到 \(k\) 各出现一次,而 \([i−k+1,j]\) 中的数已经被以 \(j\) 为结尾的段确定了,剩下 \(i−j\) 个数任意指定次序。(注意这里是被计算贡献的以 \(j\) 结尾的排列数,也就是说上一个排列一定在 \(j - k\) 及之前结尾,不然以 \(j\) 结尾的排列会被覆盖,从而没被计数)
所以,\(f_i = k! \times k^{i-k} - \sum_{j = i - k + 1}^{i - 1} f_j \times (i-j)!\)
最后,由于为了转移,\(f\) 仅考虑了前 \(i\) 个元素的填法,统计答案时 \(ans = \sum f_i \times k^{n - i}\)
10.27
Edu 153
A
猜测只用考虑 \(()()()()\cdots\) 和 \(((((\ \cdots ))))\) 这两种可能答案,因为除了 \((,\ ),\ ()\),两者没有公共子串,因此,不存在一个串同时是两者的子串
倒是 \(string\) 的用法需要注意一下
s.find(t, pos) 返回从 pos 开始的第一个 t 出现的位置,如果没找到,返回 npos(即 \(-1\))
B
首先考虑最优解肯定是尽量选价值为 \(k\) 的金币,剩下的再用价值为一的金币,即最优情况下,我们应用 \(\lfloor m/k \rfloor\) 个价值为 \(k\) 的金币,再用 \(m \bmod k\) 个价值为 \(1\) 的金币,凑出 \(m\)
但是,如果价值为 \(k\) 的普通金币不够用,我们可以用价值为 \(1\) 的普通金币来换。
即,
coin_k_needed = m / k; // 需要的价值为k的金币
coin_1_needed = m % k; // 需要的价值为1的金币
coin_k_fancy = max(0, coin_k_needed - ak); // 需要的价值为k的fancy币
coin_1_fancy = max(0, coin_1_needed - a1); // 需要的价值为1的fancy币
coin_1_left = max(0, a1 - coin_1_needed); // 剩下的价值为1的普通金币
exchange = min(coin_k_fancy, coin_1_left / k); // 能够通过剩下的价值为1的普通金币兑换的价值为k的fancy币
ans = coin_1_fancy + coin_k_fancy - exchange
C
博弈论原理的应用:如果一个局面能够转移到的状态都是必败态,则该状态是必胜态,否则,如果能够转移到至少一个必胜态,则该状态是必败态
在本题中,一个数可以转移到在它前面且比他小的数,那我们只需要判断这些数是不是都必败即可
具体地,使用两个树状数组,分别维护值在 \([1, x]\) 的数的个数,以及值在 \([1, x]\) 的 unlucky 数的个数,对于每个数,判断 ask_1(a[i] - 1) == ask_2(a[i] - 1) 即可
D
首先,我们先统计出序列中 \(01\) 以及 \(10\) 的个数,即每个 \(1\) 左边的 \(0\) 的个数之和,以及每个\(1\) 右边的 \(0\) 的个数之和,那么我们的目标是让这两个值相等,于是我们考虑他们的差值,目标即为让这个差值变为 \(0\)
接着,我们考虑交换操作对这个差值的影响,分析后可以发现,若交换一个 \(1, 0\)(\(1\) 在 \(0\) 之前),则差值将会增大 \(2(p_0 - p_1)\),反之,如果交换 \(0, 1\),差值将会减小 \(2(p_1 - p_0)\),即,差值的变化量 \(\Delta = 2(p_0 - p_1)\)
那么,我们的问题就变成,有若干个二元组 \((p_0, p_1)\),每个的价值为 \(2(p_0 - p_1)\),选出其中的一些,使得价值总和为 \(k\)(初始的差值),求选择的二元组数量的最小值,并且,不能选两个有公共端点的二元组,即 \(\forall (p_0, p_1), (q_0, q_1) \in T, p_0 \ne q_0, p_1 \ne q_1\)( \(T\) 是选出的集合)
然后不会做了,这个限制太难搞了
我们从另一个角度考虑,这个限制的关键在于不能选有两个公共端点的二元组,实际上,我们细品这句话,它等价于每个点只能被选一次
这样就好做多了,原来的价值定义为 \(2\sum (p_{i0} - p_{i1})\),那么,如果我们将其看成若干个点,代价就是:
\(p_i\) 是我们所选第 \(i\) 个点的下标
于是,我们需要上式等于 \(k\),并且所选的点中系数为 \(1\) 和 \(-1\) 的点数量相等,求满足上述要求所需要的最少系数为正的点
于是,定义 \(f(i, j, k)\) 为仅考虑串中前 \(i\) 个字符,正数点与负数点个数差值为 \(j\),总价值为 \(k\) 所需要的最少系数为正的点
那么:
于是这题就愉快的做完了
题解中的方法则是更加基于对序列特征的刻画
定义 \(f(i, a, b)\) 为仅考虑串中前 \(i\) 个字符,序列中有 \(a\) 个 \(0\),有 \(b\) 个 \(01\) 时,与原串不同的位置数量的最小值
考虑顺转,
若第 \(i + 1\) 位填 \(0\),则 \(f(i + 1, a + 1, b) += f(i, a, b)\)
若第 \(i + 1\) 位填 \(1\),则 \(f(i + 1, a, b + a) += f(i, a, b)\),因为前面的每个 \(0\) 添上新的这个 \(1\) 都能构成一个新的 \(01\)
那么我们最后如何得到答案呢?
设原串中 \(0\) 的个数为 \(x\),长度为 \(n\),那么最后我们的 \(01\) 的个数应该等于所有 \(0, 1\) 的组合地一半,即 \(\frac{x(n-x)}{2}\)
那么答案就是 \(f(n, x, \frac{x(n - x)}{2})\) ......
还要除以 \(2\) !因为 \(f\) 记的是不同位置个数,而交换一次最多能复位两个元素,所以除以 \(2\)
Edu 152
C
字符串操作,使用哈希
实际上有更简单的做法,我们维护每个串实际上修改的区间
因为如果要排序的区间 \([l, r]\) 的开头是一段连续的 \(0\),那么排序完后这些连续的 \(0\) 应该还在原位,所以是不变的,结尾同理,即,实际修改的区间的左端点是 \([l, n]\) 中第一个 \(1\),而右端点是 \([1, r]\) 中最后一个 \(0\)
特别的,如果新的左右端点不能构成一个区间,说明不会有修改
那我们只需要看有多少对不同的新左右端点即可
vp的时候发现了性质,但是研究的是两个串不公共部分的性质,实际上这可以转到每个串自己身上,但没有想到
D
还是充分利用性质,发现如果一段区间有 \(2\),那么可以花 \(1\) 的代价将 \(2\) 左边第一个 \(0\) 到右边第一个 \(0\) 之间的区间(包括端点)染成红色,如果有 \(1\) 无 \(2\),那么可以花 \(1\) 的代价将 \(1\) 左边第一个 \(0\) 到右边第一个 \(0\) 之间的区间(只包括一个端点)染成红色
那么我们只需要从前往后扫一遍,如果当前区间能够用 \(1\) 的代价染色,就一直扩展右边界,否则就将答案加一,左端点跳到右端点之后
10.28
Edu 151
D
设 \(s_i = \sum_{i=1}^{i} a_i\),那么我们可以发现最优的 \(k\) 一定出现在 \(s\) 之中,即 \(\exists s_i = k_{opt}\)
然后我就一直在考虑最优点的位置的特点,没有进展
一万年后,换了种思路,我们发现在前面的限制下,当我们令 \(k = s_i\) 时,答案就是 \(k + (\ [i + 1, n]\) 以 \(0\) 为基准计算得到的答案 \()\)
那我们设 \(f_i = [i + 1, n]\) 以 \(0\) 为基准计算得到的答案,考虑如何转移(vp时一直在想暴力的做法,没往转移上想T_T)
为了转移,我们的基准不能变,考虑 \(f_i \leftarrow f_j \ (j > i)\) 那么如果 \(\sum_{k=i}^{j - 1} a_k > 0\),那么后面的基准就不再是 \(0\),所以我们应该将 \(i\) 到第一次 Rating 降到 \(s_i\) 以下的位置划为一段,那这部分对 Rating 的修改就是无效的,因为最终回到了 \(s_i\),然后后面的部分就可以转移过来了,如果后面 Rating 不会降到 \(s_i\),那么 \(f_i\) 就等于后面所有数的和,最后答案就是 \(\max_{1 \leq i < n}\{s_i + f_{i+1}\}\)
上述算法可用线段树实现 \(O(n\lg n)\)
但是我们如果更深入地考虑转移的过程,可以将其优化到 \(O(n)\) 的
每次,我们都会选择和小于 \(0\) 的一段数,将其跳过,那么实际上 \(f_i\) 就等于 \(sum[x, n]\),\(x\) 是满足以下条件最靠前的位置:\(\forall y \geq x,\ sum[x, y] > 0\),进一步考虑,实际上前面的部分 \([i, x - 1]\) 实际上就是以 \(i\) 开头的最小前缀和,所以 \(s_{i - 1} + f_i\) 就是序列总和减去以 \(i\) 开头的最小前缀和,那么,遍历所有 \(i\) 之后,我们实际上得到的就是序列的 总和减最小子段和
那我们就只需要求出最小子段和即可,答案就是最小子段左端点 \(-1\) 的前缀和
10.29
Edu 150
D
C题一直在调,浪费了很长时间
最小值的最大值,考虑二分,即判定能否使数列中全部数都大于等于 \(x\)
于是我们考虑让每个数都变得尽可能大(原来我考虑的是小于 \(x\) 的数,不太好想)
我们观察这个操作的特点,容易发现只有当一个数被操作奇数次,它才会变大,否则就会变小,并且增大的值等于最后一次操作的编号减去 操作次数 除以 \(2\)
这样的话,我们发现不是所有的数都能变大,只有当 \(n \equiv k \pmod 2\) 时,才有可能每个数都变大
我们重新考虑操作的特点,相当于加上最大操作,然后加上一堆 \(-1\),那我们可以考虑将每个操作分配到每个数上,那么显然大的操作应该分到小数上,并且,由于我们想让所有数都变得尽可能大,我们就将最大操作 \(k\) 分配给最小值,将 \(k - 1\) 分配给次小值,以此类推,即 \(k \sim k - n + 1\) 全部从小到大被分给不同的数
那么剩下的数都只会两两组成 \(-1\),使值变得更小,那我们的 \(check\) 就只需要判断每个数(加上第一次分配的操作之后)与 \(x\) 的差之和,是否大于 \(-1\) 的个数(即 \((k - n) / 2\))即可,实际上连判断也不需要,可以用公式计算出来
特别的,对于 \(k < n\) 的情况,只需要对前 \(k\) 小的元素加就行了
另一种情况是 \(k \not\equiv n \pmod 2\),这是,一定有一个元素是变小的,也就是说,我们第一轮分配的时候要少分配一个元素,根据贪心,我们选择舍弃分配给最大值的操作,然后按原来方法计算即可

浙公网安备 33010602011771号