石中训练:思维

CF1082E

思维难度不小。

先从暴力入手,找个左,右端点。复杂度 O(n2)O(n^2),就不太行。看只固定一个端点行不行。我们将总共的答案设为 ansans

设原来序列中有 hh 个数为 cc,如果我们选了一段区间,就会有 xx 个数被修改成 cc,也会有 yycc 被修改成其他数。那么 ans=max(h+xy)ans=\max(h+x-y)

hh 一定,只要求最大的 xyx-y 就好了。从动态规划考虑,设 dpidp_i 为以 ii 结尾的区间最大的 xyx-y。现在要找区间左边的。

1 5 5 5 1

dp5dp_5 的时候,显然要枚举 141\sim 4。但这不就 n2n^2 了吗?如果只钦定 a1a_1 作为众数呢?答案似乎不对,但事实可行。

因为众数为 55 的区间肯定没必要包含 a5a_5a24a_{2\sim4} 就可以了。由此可以得到结论:因为左,右端点延伸到非 aia_i 的数,答案也不会更优,所以众数为 aia_i 的最小区间,该区间左右端点肯定也为 aia_i

所以真正的状态应该是 dpidp_i 为以 ii 为结尾的区间,设定众数为 aia_i 的最大 xyx-y。根据上文结论,知道 dpidp_i 从上一个 aia_i(称为 alasta_{last})可以得到最优的,类似最大子段和思想,可以选择 alastia_{last\sim i},也可以抛弃前面的,直接选 aia_i 一个。再使用前缀和,快速求出任意区间 yy 的数量。

综上,状态转移方程为 dpi=max{dplast(si1slst)+1,1}dp_i=max\{dp_{last}-(s_{i-1}-s_{lst})+1,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;
}
posted @ 2023-10-14 16:32  cjrqwq  阅读(16)  评论(0)    收藏  举报  来源