CF2136C

这道题的解法是动态规划。
我们设 \(dp_i\) 表示当前遍历到第 \(i\) 位的最长的 \(neat\) 长度。
如果不选当前这个位置的数,答案应为前一个个位置的答案。
如果选了当前数,只有在能与前面的数组成一个的情况下才可以对答案产生贡献。
所以我们开一个辅助数组 \(cnt\) 记录当前每个数的出现次数,再用一个数组 \(b\) 记录每个数值的出现位置。
接着如果当前数值的出现次数可以构成一个块,就更新答案。
设这个块的从 \(p\) 位置开始,则转移方程为:\(dp_i=\max(dp_{i-1},dp_{x-1}+a_i)\)
代码如下:

#include<bits/stdc++.h>
#define re register
#define ll long long
//#define int ll
using namespace std;
int t,n,a[200001],dp[200001],cnt[200001];
vector<int>b[200001];
signed main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>t;
	while(t--){
		memset(cnt,0,sizeof cnt);
		for(int i=1;i<=n;i++)b[i].clear();
		cin>>n;
		for(int i=1;i<=n;i++)cin>>a[i],b[a[i]].push_back(i);
		for(int i=1;i<=n;i++){
			dp[i]=dp[i-1],cnt[a[i]]++;
			if(cnt[a[i]]>=a[i]){
				int k=b[a[i]][cnt[a[i]]-a[i]];
				dp[i]=max(dp[i],dp[k-1]+a[i]);
			}
			//cout<<dp[i]<<' ';
		}
		cout<<dp[n]<<'\n';
	}
}

posted @ 2025-08-29 15:48  zhuoheng  阅读(20)  评论(0)    收藏  举报