题解:P11233 [CSP-S 2024] 染色
本文仅供自我复习,一年后来看感觉写复杂了,懒得改了。
基本思路
错误的状态设计
经过审题,发现可以粗略地设计一下动态规划的状态转移方程。场上使用的是二维的状态转移,即令 \(f(i,j)\) 表示做到前 \(i\) 个,\([i-j+1,i]\) 这段区间相同颜色的方案数,有转移如下:
后面发现无论你怎么优化,时间都会卡在 \(O(n^2)\),这时就应该考虑优化状态设计本身了。
UPD:这个东西如果改一下定义 \(f(i,j)\) 表示前 \(i\) 个,\([j+1,i]\) 这段颜色相同的方案数那么还是挺可做的,但是场上设计的第二维相对数值导致难以直接发现优化。
正确的状态设计
朴素地设计状态,令 \(f(i)\) 表示做了前 \(i\) 个的最大获利。
可以得到:
其中 \(g(l,r)\) 表示 \(\begin{aligned}\sum_{i=l+1}^r a_i*[a_i=a_{i-1}] \end{aligned}\)。
具体推导过程如下:
首先显然地,\(f(i)\geq f(i-1)+a_i*[a_i=a_i-1]\),因为可以选择不更换颜色,和上一个一样,那么因为 \(a_i>0\),\(f(i)=f(i-1)+a_i*[a_i=a_{i-1}]\) 是至少可以达成的。这样这个状态就有了一个重要性,即单调不减性。
再考虑第 \(i\) 个颜色最近与第 \(j(j\le i-2)\) 个一样的情况,这样就有
具体来说,中间的 \(g(j+1,i-1)\) 是因为 \(i\) 和 \(j\) 之间是一段连续的颜色,计算它们的贡献的值即为 \(g(j+1,i-1)\)。
现在状态是 \(O(n)\),单次转移是 \(O(n)\) 的,到这里其实已经可以写个单调队列之类的东西直接做了,但是还有更好的方法,将单次转移变成 \(O(1)\)。
更优的转移
具体地,我们利用单调不减性,发现相邻两个状态如 \(f(j)\) 和 \(f(j-1)\) 至少相差 \(a_j*[a_j=a_{j-1}]\),扩展一下可以得到对于\(j<i\),\(f(i)-f(j)\geq g(j,i)\)。
观察第二个转移方程,其实我们发现,\(f(i-1)\) 已经涵盖了前面所有可能颜色段情况的最大值,但是并没有考虑到如果存在 \(a_j=a_i\) 时可以将转移加上 \(a_i\)。我们要做的只是找到前面所有的 \(j\),这样所有的 \(j\) 取值就从 \([1,i-2]\) 变成了 \(\forall j\in[1,i-2],a_i=a_j\)。
然而我们还可以进一步优化,我们有结论如下:在第二个转移方程中,\(j\) 取左侧最近一个 \(a_i=a_j\) 的位置一定是最优的。为什么呢?我们做如下分析:
我们不妨称左侧最近一个 \(a_i=a_p\) 的位置为 \(p=pos_{a_i}\),显然这是可以直接处理出来的。
考虑取 \(j<p\)。
取到 \(p\) 的贡献:\(f(p+1)+a_i*[a_i=a_p]+g(p+1,i-1)\)
取到 \(j\) 的贡献:\(f(j+1)+a_i*[a_i=a_j]+g(j+1,i-1)\)
两式相减,
由上方推理, \(f(p+1)-f(j+1)\geq g(j+1,p+1)\)。
由 \(p\) 的定义,\(a_i*[a_i=a_p]-a_i*[a_i=a_j]\geq 0\)。
由 \(g\) 的定义,\(g(p+1,i-1)-g(j+1,i-1)=-g(j+1,p)\)。
所以最终值为 \(g(j+1,p+1)-g(j+1,p)= a_p*[a_p=a_{p-1}]\geq 0\)。
我们证明了取 \(p\) 前面的一定不比 \(p\) 更优。
那么取后面呢?后面的情况有可能比 \(p\) 更优,但是一定不比来自 \(f(i-1)\) 的转移更优,因为来自 \(f(i-1)\) 包含了一切前面任何颜色段的转移,它只是没有考虑如果有 \(a_p=a_i\) 时转移可以加上 \(a_p\) 这一特点而已,这在一开始分析时已经强调过。
至此,我们就完成了优化部分的证明。
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e5+5,M=1e6+5;
int t,n,a[N];
LL dp[N],g[N],lst[M];
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);lst[a[i]]=0;
g[i]=g[i-1]+(a[i]==a[i-1])*a[i];
}
for(int i=1;i<=n;i++){
dp[i]=dp[i-1]+(a[i]==a[i-1])*a[i];
if(lst[a[i]]&&lst[a[i]]<=i-2)
dp[i]=max(dp[i],dp[lst[a[i]]+1]+g[i-1]-g[lst[a[i]]+1]+a[i]);
lst[a[i]]=i;
}
printf("%lld\n",dp[n]);
}
return 0;
}

浙公网安备 33010602011771号