题解:P11233 [CSP-S 2024] 染色
这篇题解的做法是作者在考场上想的思路的完善(虽然场上没做出来)。
考场思路
在考场上,作者先很快地设出了状态:
然而,写到一半,作者发现不对劲,然后发现了以下两个问题:
-
-
区间
[j+1,i-1] 的贡献与前面的染色方法有关,因为A_{j+1} 的左边第一个同色数字无法确定。
然后场上就放弃了,打了暴力。
可是回家路上,作者很快就想出了一些方法来规避这两个问题:
-
对于第一个问题,它其实并不是问题。因为由最近的
A_j 来转移肯定更优。例如1 2 1 3 1,如果把A_1,A_5 染红,A_2,A_3,A_4 染蓝,那不如直接把A_3 也染红,稳赚不赔。 -
对于第二个问题,可以通过状态的设计使它没有后效性。
正确思路
容易发现,如果存在一段连续的区间
为什么要这么操作?主要是为了方便转移,这样染成同的一段区间,除了第一个数都没有贡献了。
设计状态
为什么会想到这个状态呢?因为作者在考场上碰到的第二个问题,
现在设计转移方程如下:
其中,
这个转移方程式十分就好理解的。不理解的可以反复看几遍状态的叙述。
这样就做完了,复杂度
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2e5 + 5, maxv = 1e6 + 5;
ll a[maxn], cnt[maxn], dp[maxn][2];
int pv[maxv], pre[maxn];
int main(){
int T;
scanf("%d", &T);
while(T --){
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i ++){
scanf("%d", &a[i]);
if(a[i] == a[i - 1]) cnt[i - 1] ++, i --, n --;
}
ll sum = 0;
for(int i = 1; i <= n; i ++) pre[i] = pv[a[i]], pv[a[i]] = i, sum += a[i] * cnt[i];
dp[0][0] = 0, dp[0][1] = -(ll)1e18;
for(int i = 1; i <= n; i ++){
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + a[i]);
dp[i][1] = -(ll)1e18;
if(pre[i + 1]) dp[i][1] = max(dp[pre[i + 1]][0], dp[pre[i + 1]][1] + a[pre[i + 1] + 1]);
}
printf("%lld\n", dp[n][0] + sum);
for(int i = 1; i <= n; i ++) pv[a[i]] = cnt[i] = pre[i] = dp[i][0] = dp[i][1] = 0;
}
return 0;
}