笑点解析:dp 题单还是我暑假拉的。
CF1989E
有一个任意构造的 \(a\) 序列,需要保证 \([1,k]\) 中每个数至少出现一次,\(b_i\) 定义为距离 \(a_i\) 最近不同元素的距离,求不同 \(b\) 的个数,对 \(998244353\) 取模。
\(2\leq n \leq 2\times 10^5,2\leq k \leq \min(n,10)\)
想一半看了题解。。。
首先 \(a\) 的元素具体是什么肯定不重要,我们只关心它在 \(b\) 中的分段情况,每一个分块,可以看做是一个先上升后下降的等差数列,形似 \(1,2,\dots,\frac{L}{2},\dots,2,1\),这样就是这样的段拼接在一起形成 \(b\) 序列,转移很简单,不考虑段数 \(f(i) = \sum_{j=1}^{i-1} f(j)\),这个显然可以前缀和优化。但是考虑到一个长度为 \(2\) 和两个长度为 \(1\) 的段同样都是 \(1~1\),所以每次前缀和之后需要减去重复方案。
到这里,我们还没有考虑分成至少 \(k\) 段的要求,考虑到这个 \(k\) 很小,所以我们用全部答案减去小于 \(k\) 的答案就好了,对于小于 \(k\) 的求解,我们令 \(g_{i,j}\) 表示到第 \(i\) 个位置,已经分了 \(j\) 段的方案数,\(pre_{i,j} = \sum_{k=1}^{i-1} g_{k,j}\) 做一下前缀和优化,然后每次再减一下重复情况就没了。
复杂度 \(O(nk)\)。
Submission
CF833B
将 \(n\) 个数划分为恰好 \(k\) 段,每段的价值定义为段内不同数个数,求价值最大值。
\(1 \leq k \leq 50,1\leq n \leq 3.5\times 10^5\)
算是切了吧,代码实现参考了一下题解。
设计 \(f_{i,j}\) 表示前 \(i\) 个位置划分为 \(j\) 段的最大权,显然有转移 \(f_{i,k} = \max \left \{ f_{j-1,k-1} + \sum_{p=j}^i [pre_{a_p} < j]\right \}\),其中 \(pre_{a_i}\) 表示 \(a_i\) 上一次的出现位置,\(\max\) 中后半部分的计数就是 \([i,j]\) 区间的贡献,这样转移是 \(O(n^2k)\) 的,显然无法通过,考虑优化。
我这里还多拆了一步,把后面拆成前缀和形式其实会更好理解,但也可以不拆,我们考虑到 \(i\) 位置在 \(i-1\) 的答案对前面的答案进行添加,发现后半部分式子当且仅当 \(pre_{a_i} < j \leq i\) 时,里面的 \(j\) 会产生 \(1\) 的贡献,做一下下标位移,我们直接对 \([pre_{a_i},i-1]\) 区间的答案区间加 \(1\) 就好了,发现这个东西可以用线段树维护,所以扔上线段树,复杂度 \(O(nk\log n)\)。
具体实现时,可以先枚举分割段数,每次重构线段树,代码实现相对更加简单。
Submission
P1415
给定一个数字串,在某些两个数字之间添加逗号,使形成的数列严格单调上升,并且字典序最大。
\(1 \leq l \leq 500\)
模拟赛因为 T3 唐诗结论没调出来,所以没读题。。。。
问题转化一下,显然是要求每一个位置的最靠后的分割位置,直接 dp 所暴露出的问题就是不符合严格单调上升的东西,dp 出的序列到最后不合法。所以我们需要先求出 最小起始分割位置 保证在这个位置之后划分的位置后面都是可以合法的。
我们设 \(f_i\) 表示前 \(i\) 个数拆分,最后一个数的最小划分位置为 \(f_i\),那么有转移 \(f_i = \max \left \{ j [num(f_{j-1},j-1) < num(j,i)]\right \}\),特别地,我们令 \(f_1 = 1\),这样,我们就有了 \(f_i\)。
然后我们设 \(dp_i\) 表示后 \(n-i+1\) 个数拆分,第一个数的最大划分位置为 \(dp_i\),那么我们同样有转移 \(dp_i = \max_{i=1}^{f_n-1} \left \{ j [num(i,j) < num(j+1,dp_{j+1})]\right \}\),特别地,有 \(dp_{f_n} =n\)。
然而这样还没有做完,我们考虑一个例子类似 1234050
,正确的划分应该为 12,34,050
,而我们并没有考虑最后 050
的划分,所以我们要对最后一个数的前导零做特殊处理,即令其 \(dp_i = n\)。
复杂度 \(O(n^3)\)。
code
P9871
给定 \(m\) 个得分区间,区间全部覆盖才能得分,不能连续覆盖超过 \(k\) 个位置,一共有 \(n\) 个位置,求得分最大值。
\(1 \leq n\leq 10^9\),\(1 \leq m,k \leq 10^5\)
看过好几遍了,就是线段树优化 dp 的板子吧。
考虑 \(f_i\) 表示前 \(i\) 个位置的最大得分,显然有 \(f_i = \max (f_i,f_{i-1})\),然后考虑暴力枚举转移左端点 \(j\),对于 \(j \geq i-k+1\),有转移 \(f_i = \max \left \{ f_{j-2}-(i-j+1)\times d+w_{j,i} \right \}\),对于 \(j \leq i-k+1\),我们在第一个转移中已经进行了。
这样转移是 \(O(n^2k)\),由于 \(n\leq 10^9\),每次能够产生贡献的也只有区间左右端点,所以很自然离散化,复杂度变为 \(O(m^2k)\),用线段树或者树状数组可以很简单地维护 \(w_{j,i}\),具体地,线段树上每一个叶节点 \(j\) 表示从 \(j\) 点开始走到当前节点带来的贡献,显然每一次对 \([1,l_i]\) 区间加法就好了,此时复杂度为 \(O(mk\log m)\),那么我们的转移就是对 \(j\in [i,i-k+1]\) 做转移,这个东西显然可以拆开扔上线段树做区间 \(\max\),更加具体地,我们把与 \(i\) 有关的分离出来,转移变成 \(f_i = -(i+1)\times d + \max \left \{ f_{j-2}+j\times d + w_{j,i}\right \}\),拿线段树维护后面式子就好了,就把刚才计算 \(w_{j,i}\) 的线段树上的 \(i\) 每次再加上 \(f_{i-2} + i \times d\) 即可。
复杂度 \(O(m\log m)\),可以通过。
code
CF2066C
给定一个序列 \(\left \{ a \right \}\),每次需要对 \(P,Q,R\) 三个变量中的一个做 \(\oplus a_i\) 操作,要求任意时刻 \(P,Q,R\) 至少有两个数值相等,求操作方案数。
\(n \leq 2\times 10^5\)
分析性质,令 \(p_i\) 表示 \(a_i\) 的前缀异或和,则任意时刻有 \(P \oplus Q \oplus R =p_i\),根据这个性质,每一个 \(PQR\) 数对都能被表示为 \((p_i,x,x)\) 的样子,设 \(f_{i,x}\) 表示第 \(i\) 个位置相同位置的取值为 \(x\)。
考虑转移,数对 \((p_i,x,x)\) 一定会由 \((p_i \oplus a_i,x,x)\) 或 \((p_i,x\oplus a_i,x)\) 转移而来,发现第一个其实就是数对 \((p_{i-1},x,x)\),也就是 \(f_{i-1,x}\),所以有转移 \(f_{i,x} \gets f_{i-1,x}\),对于第二个数对,我们分类讨论,因为上一个数对也要有两个相同数,所以 \(x\) 只能为 \(p_{i-1}\) 或 \(p_i\),\(p_i\) 的方案已经算过了,考虑 \(x=p_{i-1}\) 的方案数,那么上一个数对一定是形似 \((p_{i-1},p_{i-1},p_{i-1})\) 或 \((p_{i-1},p_i,p_{i-1})\) 的,对于第一个,有 \(3\) 种方法,对于第二个,有两种方法,所以有转移 \(f_{i,p_{i-1}} = f_{i-1,p_{i-1}} \times 3 + f_{i-1,p_i} \times 2\)。滚动数组压一下用 map
存就好了。
Submission
[AGC033D] Complexity
好题。
首先有一个显然的做法,设计 \(f_{i,j,x,y}\) 表示左上角是 \((i,j)\) 右下角是 \((x,y)\) 的最小答案,然后可以枚举矩形边长和分割线做到 \(O(n^5)\) 的复杂度,无法通过。
考虑优化以上做法,一个最劣的情况是所有位置周围的数字全部与当前位置不同,那么我们是每两个数字一合并最优,这只会合并 \(\log (nm)\) 轮,所以答案的上界只有 \(\log (nm)\)。
发现了这个性质之后,我们就可以优化上述做法了,在一个大矩形枚举的时候,因为可能最优的转移位置只有 \(\log\) 个,所以枚举分割线可以省去,复杂度是 \(O(n^4\log n)\) 的,但是时间优化了空间不够。
再换一个想法,要优化空间就把答案扔到状态上,重新改一下状态,\(f_{k,u,l,r}\) 表示第 \(u\) 行及其往下,\([l,r]\) 这个范围内合并不超过 \(k\) 次所能到达的最大行数是多少。
这个状态就很巧了,对于答案的判定,我们枚举答案,然后看 \(f_{k,1,1,m}\) 是否能扩展到 \(n\) 即可。
由 \(k=0\) 开始看,我们首先要预处理出这个东西,考虑直接枚举 \(u,l\),然后从 \(l\) 开始枚举 \(r\),因为每次向右走的时候最大扩展位置一定单调不增,所以额外开一个变量每次减就好了,复杂度是 \(O(n^3)\),而且卡不满。
然后是转移,首先有 \(f_{k,u,l,r} \gets f_{k-1,u,l,r}\),然后考虑枚举行来分割的情况,最优的显然是这一部分 \(k-1\) 答案的下一行也拿 \(k-1\) 来扩展,写出来就是 \(f_{k,u,l,r} \gets f_{k-1,f_{k-1,u,l,r}+1,l,r}\)。
对于枚举列来分割的情况,我们还是先枚举分割点 \(x\),那么最远扩展位置是 \(f_{k,u,l,r} \gets \min(f_{k-1,u,l,x},f_{k-1,u,x+1,r})\),之后对以上说的转移取 \(\max\) 就做完了。
时间复杂度 \(O(n^4 \log n)\),空间 \(O(n^3\log n)\),可以通过,可能需要卡卡常,我跑的还挺快的。
code