前言
直面恐惧!
思路分析
首先考虑这个形式就很像区间划分,所以考虑设 \(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;
}