题解:[CSP-S 2024] 染色

前言

直面恐惧!

思路分析

首先考虑这个形式就很像区间划分,所以考虑设 \(f_i\) 表示考虑前 \(i\) 个元素的最大价值。

考虑怎么转移,一个 naive 的想法是这样:

\[f_i=\max_{j=1}^{i-1} f_j+w(j,i) a_i=a_j \]

\[f_i=\max(f_i,f_{i-1}) \]

其中 \(w(i,j)\) 表示令 \(a_i\)\(a_j\) 颜色相同,中间的数颜色不同的价值。

但是这个东西假的离谱,具体 hack 详见样例一的第三个。

考虑问题出在哪里,不难发现,是这种情况:

\[1,2,3,2,3 \]

此时 \(2\) 可以配对,\(3\) 也可以配对。

于是我们对 DP 转移稍加修改:

\[f_i=\max_{j=1}^{i-2} f_{j+1}+w(i,j),a_i=a_j \]

\[f_i=\max(f_i,f_{i-1}+a_i),a_i=a_{i-1} \]

\[f_i=\max(f_i,f_{i-1}) \]

解释也很简单,我们令 \(a_i=a_j\) 不会影响 \(a_{i+1}\) 的转移,但是 \(a_{i-1}=a_i\) 的情况需要特殊考虑。

然后到这里其实就可以用桶维护了,但是我们想要更优美一些。

观察发现,\(f\) 是单调递增的,因此我们只需要考虑满足 \(a_j=a_i\) 的最大的 \(j\),也就是 \(lst_{a_i}\)

复杂度 \(O(n)\)

代码实现

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e15;
int t,n,sum[200005],a[200005],lst[1000005],f[200005],ans;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			lst[a[i]]=0;
			if(a[i]==a[i-1]) sum[i]=sum[i-1]+a[i];
			else sum[i]=sum[i-1];
		}
		for(int i=1;i<=n;i++){
			f[i]=f[i-1];
			if(lst[a[i]]){
				if(lst[a[i]]!=i-1) f[i]=max(f[i],f[lst[a[i]]+1]+sum[i-1]+a[i]-sum[lst[a[i]]+1]);
				else f[i]=f[i-1]+a[i];
			}
			lst[a[i]]=i;
		}
		ans=sum[n];
		for(int i=1;i<=n;i++){
			ans=max(ans,f[i]);
		}
		cout<<ans<<'\n';
	}
	return 0;
}
posted @ 2025-03-15 20:29  _Kenma  阅读(24)  评论(0)    收藏  举报