石中训练:思维
思维难度不小。
先从暴力入手,找个左,右端点。复杂度 ,就不太行。看只固定一个端点行不行。我们将总共的答案设为 。
设原来序列中有 个数为 ,如果我们选了一段区间,就会有 个数被修改成 ,也会有 个 被修改成其他数。那么 。
一定,只要求最大的 就好了。从动态规划考虑,设 为以 结尾的区间最大的 。现在要找区间左边的。
1 5 5 5 1
在 的时候,显然要枚举 。但这不就 了吗?如果只钦定 作为众数呢?答案似乎不对,但事实可行。
因为众数为 的区间肯定没必要包含 。 就可以了。由此可以得到结论:因为左,右端点延伸到非 的数,答案也不会更优,所以众数为 的最小区间,该区间左右端点肯定也为 。
所以真正的状态应该是 为以 为结尾的区间,设定众数为 的最大 。根据上文结论,知道 从上一个 (称为 )可以得到最优的,类似最大子段和思想,可以选择 ,也可以抛弃前面的,直接选 一个。再使用前缀和,快速求出任意区间 的数量。
综上,状态转移方程为
CoDe
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+10;
int n,c,s[N],lst[N],ls[N],a[N],dp[N],ans;
int main() {
ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
cin>>n>>c;
for(int i=1;i<=n;i++) {
cin>>a[i];
s[i]=s[i-1]+(a[i]==c);
}
for(int i=1;i<=n;i++) {
if(a[i]==c) continue;
int lst=ls[a[i]];
dp[i]=max(dp[lst]-(s[i-1]-s[lst])+1,1);
ans=max(dp[i],ans);
ls[a[i]]=i;
}
cout<<s[n]+ans;
return 0;
}

浙公网安备 33010602011771号