【做题记录】ds合集 Part III

ds 合集的 Part 3,此合集包含贪心问题。

贪心问题

CF30E

题目链接

考虑对一个 a 找到其对于的 a,肯定是越前越优,那么拿 S 的反串做个 kmp 即可得到每个 a 的第一次出现位置。然后就是在区间中找最长的奇回文串,manacher 预处理,然后二分半径 len,看看 [l+len1,rlen+1] 是否存在 pilen 即可,st 表维护。复杂度 Θ(nlogn)

CF187E

题目链接

特判 l=0,1

由于最左、最右都要去,所以答案有个明显的下界是 min(xsx1,xnxs)+xnx1。假设取到 xsx1(可以翻转再做一遍),那么最优肯定是要从 s 先往左走到 1,然后跳到 s+1 再一直往右走到 n,但会出现左票不足或过多的情况。

  • 左票不足:那就是先往左走几步,然后跳到 1,往右走回来的时候再走那些没走过的。
  • 左票太多:那就是 [s+1,n] 这些位置有些要通过左票到达,那一个点在 s 右边且被左票到达肯定是往右走到了某个点然后往回走到了它,这时有可能是从 n 往回走的,那么 [xi,xi+1] 这段区间只用走 2 次,否则就要走 3 次,那么枚举终点在哪里,找到 s 到终点这段区间中前 k 小的区间换成左票即可,k 是左票剩的减去从 n 走回终点的。

可以用平衡树或线段树维护前 k 小,复杂度 Θ(nlogn)​。

CF196D

题目链接

考虑找到原串最大位置 pos 使得 s[1,pos] 没有长度大于等于 d 的回文子串,然后从 pos 开始尝试替换。注意到只用考虑长度为 d,d+1 的回文串,因为 d+2,d+3 的包含 d,d+1 的。如果 pos 往前某个位置替换成一个字符后,以它结尾的 d,d+1 的串都不是回文串,那就开始往后加字符,直接暴力枚举判断是否合法即可。复杂度 Θ(nc)​。

CF436E

题目链接

考虑反悔贪心,每次添加一个星星,有几种情况。

  • 从选了一个的加上 biai
  • 从没选的加上 ai
  • 从选了一个的和没选的加上 ai+bj
  • 从选了两个的和没选的加上 aibi+bj

用五个堆维护最小的 ai,bi,ai,aibi,biai 即可,复杂度 Θ(nlogn)

CF524F

题目链接

cnt0,cnt1 为删掉原括号串的所以合法括号子序列后的 () 的个数,显然最少添加 |cnt0cnt1| 个括号。考虑操作 2,就是要求一个字典序最小的循环同构,使得这个循环同构也只需要 |cnt0cnt1| 个括号,先不管原串中所有没匹配的字符,这个循环同构的起点一定是原串某个合法括号子段的开头,枚举这个位置,再看看这样循环移位后原来没匹配的那些字符还需要添加多少个,若为 |cnt0cnt1|,那么这个位置就是可能可行的。

考虑所有可行的位置,要找到字典序最小那个,就把原串复制一遍接到后面,然后后缀排序即可。使用 SA 常数较大,可能无法通过,使用 SAM 可以做到 Θ(n)

复杂度 Θ(n)Θ(nlogn)

CF625E

题目链接

按照位置排序后显然每个没被删掉的人相对位置不会改变,可以用链表维护这个相对位置。然后每个人先删的肯定是它下一个人,那就对每个人维护删掉它后一个人至少需要多少轮,把它放进一个小根堆里面,每次取出堆顶的人,记录时间 t,从它开始一直往后删,直到需要的时间大于 t 停止,记录被删的人数。每次把所有 t 相同的人拿出来,这样更新后,更新它们的速度,并且记录每个人上次更新速度的时间(用现在的时间减去上次更新的时间乘上速度就是当前的位置)。然后再对这一轮 t 相等的人找到它们上一个人和下一个人,更新删掉它或删掉其他人的时间,继续放到堆里面。模拟这个过程即可,复杂度 Θ(nlogn)

CF671D

题目链接

考虑权值都是 1 怎么做,显然从深的往浅的遍历每条边,如果遇到一条边没被覆盖,就找到能覆盖到这条边的且 y 最浅的那条边选择。

考虑权值任意,此时依然从深往浅做,并且每次取权值最小的那条路径。当然,直接这样是显然错误的,因为权值小的路径深度可能不如权值大的浅。那么就可以考虑一个反悔贪心的过程,每次依然贪心地选择权值最小的,并且把 x 在这个权值最小的路径的 x 下方或等于它的所有路径的 x 设为这条路径的 y,把它们的权值减去当前选的这条路径。

实现可以考虑从下往上合并,并且记录还能往上覆盖几条边和下方的所有没被选的且 y 在这个点上方的路径,还要支持单点修改和整体减。可以考虑线段树合并或平衡树来维护这个东西。复杂度 Θ(nlogn)Θ(nlog2n)

CF797F

题目链接

注意到把老鼠和洞按照编号排序,每个洞进的老鼠一定是一段连续的区间,设 dpi,j 表示前 i 个洞进了前 j 个老鼠,设 sumi,j 为洞 i 到前 j 个老鼠的距离和,转移有 dpi,j=maxk=max(0,jci)jdpi1,k+sumisumk,显然可以单调队列优化。

复杂度 Θ(nm)

CF935F

题目链接

这里设 ai 为原来的 aiai1,那么 f(a)=i=2n|ai|

考虑操作 1,特判边界。即在 alar 中选一个 aiai+x,ai+1ai+1x。讨论一下有哪些情况,设 ans 为选择一个位置加上 x 后和原序列答案的差。

  • rl+13,直接暴力跑一下
  • i,ai0,ai+10,则 ans=2x,是最优的情况。
  • i,ai0,ai+10,可以发现 ans 总非负。
    • ai+1x,则 ans=2(xai+1)
    • ai+1x,则 ans=0
  • i,ai0,ai+10,可以发现 ans 总非负。
    • aix,则 ans=2(x+ai)
    • aix,则 ans=0
  • i,ai0,ai+10,可以发现只要 aixai+1x,那么 ans 一定非正,所以只用考虑 aixai+1x,此时 ans=2x+2(aiai+1)

综上,当长度大于 3 时,必定出现正负或正正或负负,ans 一定非负,只需要维护所有满足条件的 ai+1,ai,aiai+1 的最大值即可,用线段树维护,复杂度 Θ(nlogn)

CF1051G

题目链接

考虑单个序列怎么做,因为把一个数变少可以得到 bi 的价值,那先尽可能地减少一定是不劣的。如果有若干个相同的 ai,那么肯定是最后让 b 最小的 a 最大,b 最大的 a 最小,这是容易的。但是整个序列有可能不能都变成同一种值,会分成若干段,每个极长段的形成是先把小的数变大,形成一个区间,被这个区间包含的数继续拓展这个区间,直到不能拓展为止。统计的话是容易的。

考虑对每个前缀求答案,那可以维护前面的若干值域连续段,显然加一个数最多添加一个段,有可能合并两个段。一个段内肯定是先把所有数变成这个段里的最小值,然后把它们的 b 排序,代价就是 i(aiminx)bi+ibi(lenrki)i(aiminx+rki)bi+lenibirki 的处理可以用线段树合并来做。复杂度 Θ(nlogn)

CF1137E

题目链接

显然只用管每次插入的第一个数,且每次进行操作 1 之后只用考虑第一段的第一个数。当进行操作 2 的时候,会暂时性地使最小值变成最后一段的第一个,但是这个影响会随操作 3 而逐渐消去,因为最后一段的下标大。

考虑维护一个不增的结构,对于相邻的两个下标 (x,y),每次进行操作 3 会使 axay 减少 s(yx),而 x,y 是固定的(因为操作 1 之后会把整个结构清空),所以记录 tx=axayyx 表示 s 至少为 txax 会比 ay 小。用链表+小根堆维护这个结构,每次往后插入后在链表最后插入一个 0,并把倒数两个的 tx 插入到堆里面。每次进行操作 3 时看看堆顶是否已经足够了,就将其删除。操作 1 直接情况即可。复杂度 Θ(nlogn)

CF1238G

题目链接

考虑将所有人按照时间排序,到时间 i 时先把能买的都买了,先不付款,每次从 lst 走到时间 i 时,取前 ilst 小的付款。但是会有容量限制,不能无限制地买,那么买 i 时如果已经满了,可以把前面还没用到的价格比 i 大的取消购买,反悔贪心。注意到每次从一个人走到下一个人要么只买以前一个人的,要么就至少买空一个人,所以一共只会买 Θ(n) 段相同的。用堆维护价格最小值和最大值,并用一个数组记录每个人能用的有多少即可。复杂度 Θ(nlogn)​。

CF1446D2

题目链接

注意到所有极长的拥有 2 个以上众数的段一定以全局众数为众数之一。证明:若全局众数不为其众数,可以拓展左右端点直到全局众数的出现次数等于其众数的出现次数。那么如果全局众数有不止 1 个,答案即为 n,否则设其为 x。考虑答案子区间的众数出现次数 cnt

  • cnt>B,只用考虑所有出现次数 >B 的数,有 Θ(nB) 个,考虑枚举这些数 i,把 x 设为 1i 设为 1,找到一个极长的段使得和等于 0 即可,用一个桶记录前缀和第一次出现位置,复杂度 Θ(n2B)
  • cntB,考虑枚举这个出现次数 i。从左往右枚举答案的右端点 r,双指针记录 x 出现次数为 i 的合法做端点 l 的区间,对于其他数也这样做,最小的 l 就是所有其他数的 l 区间和 xl 区间交的最小值。复杂度 Θ(nB)

复杂度 Θ(nn)

CF1469F

题目链接

考虑二分答案,然后按长度从大到小排序,依次从浅到深加入,每次加入一条链时尽量把连接点放到中心。每次连接中心是显然的,因为让 mid 的白点尽可能浅来接其他链。从大到小加入是因为每次链接的上面那个白点的深度是单调不降的,所以肯定让深度浅的时候能贡献更多,如果在深一点的地方接大的就会造成浪费了。差分维护一下即可,复杂度 Θ(nlogn)

CF1503D

题目链接

考虑无解的必要条件,若存在 ai,bin,那么必定存在一个 aj,bj>n,显然无解。那么有解必然对于所有的 i 使得 ain,bi>nai>n,bin

i[1,n] 对应的另一边(比它大的数)是 f(i),那么答案就是若干个 i 升序排列,其对应的 f(i) 降序,然后接上 f(i) 升序,i 降序。其中前面那段的 i 在反面的贡献为 1,后面那段的 i 在正面贡献为 1。那么按照 i 排序,就是要让 f(i) 分成两个下降子序列。

考虑判断是否有解,贪心地将新的 f(i)​ 加入前面两个下降子序列中最后一个数较小的且大于它的那个子序列中,但这样可能并不是最优解。

考虑什么时候一个数可以加入前面两个序列,结论:minj=1iaj>maxj=i+1naj 时,会有多种方案,这是显然的。那么考虑按照这个把原序列分段,每段有唯一分组方案,只是不知道哪个是前面的一段,哪个是后面的一段,显然讨论一下取最小即可。复杂度 Θ(n)

CF1539F

题目链接

考虑中位数的经典套路,假设当前在求 x 的答案,先假设当前区间中只有一个 x,把 <x 的数看成 1>x 的数看成 1,把 x 看成 0 那么 x 的距离就是这个区间的和除以 2 向下取整的绝对值。那么如果有多个 x,把一个 x 看成 0,把其他的全看成 1 或者 1 一定是最优的。因为考虑只有一个 x 时,若 x 排序后在中位数左边,加入一个 x 会使中位数位置往右移动,最左边那个 x 一定是最远的,x 在右边时同理,最右边肯定是最远的。

考虑扫描值域,用线段树维护前缀和,每次修改一个后缀,询问 i 时先把所有 aj=aij 修改为 11 都做一遍,询问 i 右边最大值减去左边最小值,和右边最小值减去左边最大值(因为有负数的情况)取最大即可。复杂度 Θ(nlogn)

CF1566F

题目链接

首先把所有已经包含点的区间扔掉,那么现在就剩一堆点然后一堆区间然后一堆点这样子。考虑每个点是怎么走的,可以先往左边走再回来再往右边,也可以先往右边再回来再往左边,也可以直往一个方向走。考虑两个相邻的点之间的区间,显然这些区间只可能被这两个点其中一个走,将它们按照左端点排序,左边那个点走的就是一个前缀的左端点最大值,右边的点走的就是后缀的右端点最小值。

dpi,0/1 表示做完前 i 个点,其中第 i 个点是不是先往右走(或者不往右走)。转移的话直接枚举上个点走到那个前缀,算一下后缀 min 即可,dpi,1 的话要来回走,所以注意贡献要加两次。复杂度 Θ(nlogn),在于排序。

CF1601D

题目链接

有个朴素的想法是全部按 ai 排序,先取 a 小的,但是发现这样是错误的,因为有些是 si<ai 的可以先取,然后还有 sjaj 的后取,而 si<ajaisj。那么可以尝试将人分成两类,si<ai 的和 siai 的。

还是根据刚刚的想法,设 fi 为当前高度为 ai 时的答案,把人按照 ai 排序。特殊地,对于 si<ai 的人的 fi,除了 ajsifj 的最大值的贡献,还有 si<akaisk 的所有 k 的贡献,那么只要算出有多少个 k 就行,二维数点 bit 维护。复杂度 Θ(nlogn)

CF1601E

题目链接

考虑单次询问,从 l 开始,在 l,l+k,l+2k 位置都买一张 [l,l+jk] 的最小值即可。

考虑多次询问,把所有 lmodk 同余的 l 一起做,从后往前扫描线。维护所有 pmodk=i 的所有位置的答案,当扫描到 l 的时候,ansl 设为 al,然后找到 cur=minj=ll+k1aj,把 l+k,l+2k,l+jk 的所有大于 curans 换成 cur 即可,单调栈维护。查询 l 对应的 r 时找到最大的 j 使得 l+jkr,把 [l,l+jk] 的所有关键点的 ans 加起来即可。复杂度 Θ(nlogn),在于 st 表。

CF1621F

题目链接

先把连续两个 01 的个数找出来,分别是 x,y。然后分讨一下,记 cnt0,cnt1 为总 0,1 的个数。

  • x<y

显然操作序列肯定是形如 212121(232323)2,括号处取到当且仅当 b>c。当取完连续两个 0 后序列一定长成若干个 1 连着一个 0 再有若干个 1,那就把首位的 0 去掉剩下 t0,那么当 b>c 时显然可以把这 t0 都删掉,此时剩下的 y 不会变,还会剩下首位的 0 能取就取就行。所以答案是 x×(a+b)+max(0,bc)×(t+min(yx1,cnt0t))+b

  • xy

操作序列是形如 121212(323232)[1],小括号取到当且仅当 b>c,中括号取到当且仅当 x>y。考虑小括号中最多取到多少个 32,那把相邻两个 1 中间的 0 个数统计出来,去掉 0 的,把剩下的排序。在前 y12 操作中可以尽量把中间连续的 0 变成一个 0,即设连续长度为 cnt1cnt2cntk,从头开始取 cnti1 贪心能取就取即可,设最多把 tcnt1 变成 1,答案即 y×(a+b)+max(0,bc)×t+[x>y]a

复杂度 Θ(nlogn)​,在于排序。

CF1635F

题目链接

Lii 左边最大的 j 使得 wjwiRii 右边最小的 j 使得 wjwi。那么答案一定是 (i,Li),(i,Ri) 的形式。证明:设答案为 (i,j),iLj,那么 xi<xLj<xj,wi+wLjwi+wj,因此 (i,Lj) 更优。右边的情况同理。预处理这些点对,二维数点即可。复杂度 Θ(nlogn)

CF1672H

题目链接

00 个数 x11 个数 y,每次删除一个区间,若为 0(10101010)11(01010101)0x,y1,若 1(01010)10(1010101)0,则 xy1。显然第一种更优,答案就是 max(x,y)+1,前缀和即可。复杂度 Θ(n)

CF1699E

题目链接

考虑扫描最小值 l,对每个数去看它拆开后最大值最能到多少。贪心是错误的,那么考虑 dp,设 dpi,j 为最小值为 ij 能分到最大值最小是多少,从大到小转移,dpi,j=max(dpi+1,j,dpi,ji),后面那个转移当且仅当 i|jji2 时成立,所以只有 i 的倍数会改变,那么直接转移是 Θ(mlogm) 的。考虑求答案,ans=mini(max(dpi,j)i),注意到 j 相同是 dpi,ji 降低是单调不增的,所以可以用个桶记录值用个指针扫就行。复杂度 Θ(mlogm)

CF1793E

题目链接

显然肯定是 a 最小的几个符合要求,因为如果有 x<yx 没满足 y 满足了,交换 x,y 肯定更优。那考虑二分答案,要让前 mid 个符合要求,后面 nmid 都去读没人读的书了,这时有两种情况。

  • nmidk,此时所有书都有人读,那么前 mid 个人读一本书就行了,而且这本书还有后 nmid 个人中的 nmidk+1 个人读,所以只要 nmidk+1+midamid 即可。
  • nmid<k 此时还有 kn+mid 个书要读,设 fi 为前 i 个人都满足要求最多读几本书,显然 fi=maxjiaifj+1。所以只要 fmidkn+mid 即可。

复杂度 Θ(n+qlogn)​。

CF1720E

题目链接

设一开始有 m 个颜色。

  • m<k,显然答案等于 km
  • m>k,下面证明答案不超过 2
    • 第一个矩阵以 (1,1) 开头拓展,把这个矩阵变成一个已有的颜色,拓展到最大的 (t,t) 使得 mk
    • m>k,第二个矩阵从 (t+1,t+1) 往左上拓展,变成和第一个矩阵一样的颜色,每次覆盖 2 个,所以最多使 m2
    • 若最终的 m=k1,那么把第二个矩阵的颜色变成一个没出现的颜色即可。
    • 判断答案等于 1:可以先枚举长度 len,然后对于每种颜色,算出它的最小、最大的 x,y,显然符合要求的左上角是一个矩阵,差分一下就行。

复杂度 Θ(n3)​。

CF1844F1

题目链接

注意到 c0 时从小到大一定是最优解,c<0 时从大到小是最优解,但是字典序不一定最小。那就从前往后考虑每一个 ai,枚举后面的 aj 看看能否把 aj 放到 ai 前面,令 f(x)=|xc|,只需判断 f(ajaj1)+f(aj+1aj)+f(aibi1)f(aj+1aj1)f(ajbi1)f(aiaj)=0 即可,bi1 是已经确定的位置。复杂度 Θ(n2)

F2 的结论没看懂/kk

CF1893D

题目链接

考虑对于一个单独的 (s,d) 应该怎么填,肯定是以长度 d 为一个区间,每个区间的数都不相同。

贪心地,考虑先满足 d 大的 (si,di),然后先用数量最多的 d 个颜色,先放 d 个,然后再重新取当前数量最多的 d 个颜色再放,重复这个过程。先用最多的原因是若用了数量少的,那么那个颜色可能以后就不能用,我们肯定希望当前可用的颜色种类越多越好。考虑模拟这个过程,先把所有颜色放到大根堆里面,然后每次就取出堆顶的 d 个弹出,然后把它们的个数减 1 塞回去,再重新取 d 个即可。

复杂度 Θ(nlogn)

CF1919F2

题目链接

考虑将水看成流,其实就是一个网络流模型,si,aiit,biii+1,ci。单点修改问最大流,显然可以贪心流,能流到 t 就流到 t

考虑两个区间怎么合并,需要知道什么。可以维护区间的几个信息:从这个区间流到右边的流大小,这个区间剩余的 bi 之和,从左边能流入最大的流使得不能再流了,还有答案分别记为 out,rem,in,ans。合并的时候,ans=ans1+ans2+min(cr1,out1,in2,rem2)in=min(in1,rem1+cr1min(out,cr+1),rem1+in2min(cr1,out1,in2))out=max(0,min(cr1,out1,in2)rem2)+out2。线段树维护即可,单点修改,全局查询。

复杂度 Θ(nlogn)

本文作者:FantasyNumber

本文链接:https://www.cnblogs.com/MiniLong/p/18474388

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   FantasyNumber  阅读(33)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起