Loading

P6856 「EZEC-4.5」子序列

还行。

限制有点恶心,式子也有点恶心。由于限制问题不弱于不加限制的问题,我们可以先考虑没有限制怎么算。

考虑加入一个数的贡献,显然分讨这个数在贡献中还是不在。我们设 \(f(i)\) 表示 \([1,i-1]\) 的答案,那么如果 \(i+1\) 在子序列中,首先会带来 \(f(i)\times a_{i+1}\) 的贡献,我们可以假装我们先对 \(\prod s_i\) 产生贡献。然后 \(a_{i+1}\)\(\sum s_i\) 的贡献我们需要知道每个方案的 \(\prod s_i\),也就是说我们要引入新的函数 \(g(i)\) 表示 \([1,i]\) 所有方案中的 \(\prod s_i\) 的和,那么我们会带来 \(a_{i+1}^2(g(i)+1)\) 的贡献。那么 \(f\) 的转移方程就可以写下来了:

\[f(i+1)\leftarrow (a_{i+1}+1)f(i)+a_{i+1}^2(g(i)+1) \]

现在考虑转移 \(g\)。这是简单的,我们容易写出转移方程:

\[g(i+1)\leftarrow (g(i)+1)a_{i+1}+g(i) \]

现在考虑怎么去处理限制。一个很容易的方法是枚举右端点,这样左端点被限定在了一个范围内,在限定区间中我们事可以随意选择的。还有一个厉害的容斥做法,先假定序列足够长,想想有什么简单的式子能够使得每一种子序列只贡献一次?我们考虑加上所有长度为 \(k+1\) 的区间中随便选的答案再减去所有长度为 \(k\) 的区间中随便选的答案,不难发现这样子每个合法子序列都会多加恰好一次。

不管怎样,这两种方式都需要快速算出若干个长度确定的区间的答案,且题目要求我们不能求逆元,虽然据说有黑科技可以求。直接求是很难的,我们考虑这个 dp 是否可以区间合并,如果能够满足这种性质我们就可以利用很多种数据结构进行维护。而这道题种这个式子很明显就是可以区间合并的,我们设左区间的信息为 \((f_0,g_0)\),右区间的信息为 \((f_1,g_1)\),那么不难推出合并起来的信息为 \((f_0(g_1+1)+f_1(g_0+1),g_0g_1+g_0+g_1)\)

线段树可能草不过去,我们有一个很好的 trick,就是对 \(k\) 分块,这样我们的贡献的段就只会出现在一个块中或两个块之间,这样就可以直接预处理合并算贡献了。

于是做完了,时间复杂度 \(\mathcal{O}(n)\)

posted @ 2025-09-30 15:10  lalaouye  阅读(10)  评论(0)    收藏  举报