Welcome to|

ZYStream

园龄:2年2个月粉丝:0关注:0

[CSP-S 2024] 染色 题解

题目链接

[CSP-S 2024] 染色

题解

这是一道线性 \(dp\) 问题,难点在于在具体的题目背景中抽象出实际问题,最难的地方是分类讨论。
根据题目的意思,如果第 \(i\) 位数字(\(a_{i}\))的颜色和第 \(i\) 位之前的数字(\(a_{[1,i]}\))的颜色都不同,则这个数字贡献为 \(0\),接着,如果前面有相同的颜色,再如果离第 \(i\) 位最近的相同颜色的数字和第 \(i\) 数字相同,则第 \(i\) 位数字贡献为 \(a_{i}\)。我们只讨论这个数可以有贡献时的情况,当这个数无论如何也无法贡献时不进行讨论,我们用结构体存 \(a_{i}\) 这个数字在之前出现过的位置的下标以及这个数字本身:

struct num{
ll n,last;
}a[2000005];

\(a_{i}\) 在之前没有出现过时,后文用 \(a_{i}(last)\) 表示 \(a_{i}\) 这个数字在之前出现过的位置的下标,\(a_{i}(last)=0\) ,接下来讨论以下情况

  • 两个相同的数相邻(\(a_{i}=a_{i-1}\)),这样的两个数,只要颜色相同,无论如何都会产生贡献,但是对后续有影响(请先看下文)
  • 两个相同数不相邻,存在 \(i>j\)\(a_{i}=a_{j}\) ,那么要想让 \(a_{i}\) 产生贡献,\(a_{[i+1,j-1]}\) 区间内的数字必须是同一颜色且和 \(a_{i} \ ,\ a_{j}\) 的颜色不同,那么对于 \(a_{[i+1,j-1]}\) 内的数字,如果存在两个相同的数,但这两个数又不相邻,它们的贡献就没有了。
  • 两组相同的数交叉,如果想让两组数都产生贡献,必须满足下列状态:
x,...,y,x,...,y

如果是下面这种状态,只能使其中一组产生贡献:

x,...,y,...,x,...,y

经过上面的分类讨论后,我们设 \(f_{i,j=0/1}\) 表示对于前 \(i\) 个数字,当 \(j=0\),表示第 \(i\) 个数字与第 \(a_{i}(last)\) 不产生贡献时的最优解,当 \(j=1\) 时,表示第 \(i\) 个数字与第 \(a_{i}(last)\) 产生贡献时的最优解。如果两个数字既相邻又相同,要把他们的贡献算进最后的答案里,但是在表示状态时,必须表示成没有产生贡献的状态。如果出现交叉情况,直接访问 \(a_{i}(last)\)\(a_{i}(last)+1\) 两个位置上的数值即可。易得到状态转移方程式:

  • 普遍情况:
    \( f_{i,0}=\max (f_{i-1,1}\ ,\ f_{i-1,0}) \)
    \( f_{i,1}=\max (f_{a_{i}(last),1}\ ,\ f_{{a_{i}(last)},0} \ ,\ f_{a_{i}(last)+1,1}\ ,\ f_{{a_{i}(last)+1},0})+a_{i} \)
  • 出现相邻两数的情况时:
    \( f_{i,0}=\max (f_{i-1,1}\ ,\ f_{i-1,0}) \)
    \( f_{i,1}=f_{i,0} \)
    将所有出现过的相邻相同两数加在一起,记为 \(anstmp\) ,最终答案就是 \(\max(f_{n,0} \ ,\ f_{n,1}) \ + \ anstmp\)

CODE

#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
ll t,anstmp;
ll dp[2000005][2];
ll lst[2000005];
struct num{
ll n,last;
}a[2000005];
int main(){
ll t;
scanf("%lld",&t);
while (t--){
ll n;
scanf("%lld",&n);
memset(dp,0,sizeof(dp));
memset(lst,0,sizeof(lst));
anstmp=0;
for (ll i=1;i<=n;i++){
scanf("%lld",&a[i].n);
if (lst[a[i].n]) a[i].last=lst[a[i].n];
else a[i].last=0;
lst[a[i].n]=i;
if (a[i-1].n==a[i].n) anstmp=anstmp+a[i].n;
}
for (ll i=1;i<=n;i++){
if (a[i].n==a[i-1].n){
dp[i][0]=max(dp[i-1][0],dp[i-1][1]);
dp[i][1]=dp[i-1][1];
continue;
}
else dp[i][0]=max(dp[i-1][0],dp[i-1][1]);
if (a[i].last) dp[i][1]=max(max(dp[a[i].last][0],dp[a[i].last][1]),max(dp[a[i].last+1][0],dp[a[i].last+1][1]))+a[i].n;
}
printf("%lld\n",max(dp[n][0],dp[n][1])+anstmp);
}
return 0;
}
posted @   ZYStream  阅读(31)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起