CF1858 D,E题解

D:

比赛临死时交了个假做法MLE了,很气,赛后发现WA了所以安心了。

题意: 给你一个 \(01\) 串,你可以最多修改 \(k\) 个位置,修改完后最长的 \(0\) 串和最长的 \(1\) 串记为 \(len0\)\(len1\),对于 \(a\)\(1\)\(n\) 的所有取值,输出 \(a*len0+len1\) 最大值。(n方)

又是 \(ax+y\) 的形式,曾经牛客多校也是有一道李超线段树写成了排序的假做法,WA完这道题才发现。

这题可以 \(n^2\),因此无需李超线段树,可以枚举 \(y\) 来求最大值,那么发现 \(y\) 最大是 \(n\) ,对于每个 \(y\)\(dp\) 数组存最大的 \(x\) 即可。

我的假做法的预处理和正解一样,处理 \(f0[i][j]\) 表示前 \(i\) 个位置换 \(j\)\(1\) 最长的 \(0\) 串,\(f1[i][j]\) 表示前 \(i\) 个位置换 \(j\)\(0\) 最长的 \(1\) 串, \(g0\)\(g1\) 同理,是后缀最大值。其实这样预处理完,\(dp\) 数组扫一遍就能求出来,我却傻乎乎去拿 \(x\) 排序。

还有一个和题解不太一样的地方,题解只算了 \(f0\)\(g0\),不用算 \(f1,g1\),因为 \(len1\) 直接枚举区间就好了,枚举 \(len1\) 的区间,就能知道这个区间需要改 \(j\) 个位置,然后前缀和后缀改 \(k-j\) 个位置最大的 \(len0\) 去更新 \(dp\) 数组即可。

code

E:

参考了SpaceJellyfish佬在群里讨论的 \(O(n)\) 做法,代码短且快,理解起来需要多考虑一些情况。

题意: 四个操作,1:数列末尾加一个数。2:数列末尾删除k个数。3:撤销上一个1或2操作。(没有撤销3操作这种duliu挺好)。4:询问数列中不同数字个数。

经典的求不同数字个数,如果题目中左端点固定,或者可以 \(n^2\) 枚举左端点,那我们一般用 \(f\) 数组记录每个数字第一次出现的位置。

在此题中,我们设 \(a\) 数组表示当前数列,\(b\) 数组取值 \(01\) 表示 \(i\) 位置是否是第一次出现的位置,那么 \(b\) 数组的前缀和 \(sum\) 就是要输出的答案哩。我们不用主席树而是只用一个线性的结构来记录答案,那么对于各个版本,如果修改的内容不多,我们可以记录修改的内容然后在撤销时返回即可。

观察此题的撤销操作和删除操作,我们发现操作形成的痕迹是一颗平躺着的树,从左往右长。删除是回到树上某个祖先,撤回删除操作即回到叶子处,这个祖先到叶子的路径其实都是我们当前线性结构储存的信息,我们只需要知道当前的位置 \(r\) ,这个到祖先或到叶子其实只是改变 \(r\) 的位置,\(sum\) 数组不变,\(sum[r]\) 一直是答案。添加操作则是生成一个新的枝条,因为我们用的是一个线性的储存结构,这个操作会覆盖掉树上之前的一些信息,当我们撤销添加操作时,覆盖掉的信息更新回来即可。

现在要解决的两个问题:怎么更新添加一个数的信息,覆盖了哪些原来的信息。

添加一个数,只需要考虑这个数 \(x\) 是不是第一次出现,\(f[x]\)\(0\) 当然是第一次出现,但 \(f[x]\) 有值时,虽然记录了 \(x\) 第一次出现的位置,但是由于 \(f\) 可能是之前版本的 \(f\) ,在删除操作之后,\(f\) 的位置指向了当前位置的后面,或者 \(f\) 位置在当前位置前面,但是那个位置已经被新版本给改成其他数字了,这种情况下 \(x\) 其实都是第一次出现。

覆盖了哪些信息呢?新位置 \(r\) 需要存当前的答案 \(ans\),那么之前的 \(ans\) 被覆盖了,从 \(f\) 更新过程来看,\(a\) 数组也是需要存的,同样被覆盖了,然后还有一个 \(f[x]\) 也被修改了,如果我们撤销这个 \(x\) 的添加,\(f[x]\) 之前应该是另外一个位置。

用栈存操作时顺便记录下这个操作覆盖的这三个原有信息,更新回来即可。

code 短小可爱

posted @ 2023-08-16 21:26  maple276  阅读(20)  评论(0编辑  收藏  举报