E. Increasing Frequency

题目链接:https://codeforces.com/contest/1082/problem/E

题意:

给出n个数以及一个c,现在可以对一个区间上的数同时加上或减去一个值,问最后c的最多数量为多少。

题解:

这题挺有意思的,我们通过分析题目可以发现,对于每一个d,如果把[l,r]里面的d都变为c,则最后c的数量为cnt(c,1,l-1)+cnt(c,r+1,n)+cnt(d,l,r)。

这个式子变化一下,有:cnt(c,1,n)+cnt(d,l,r)-cnt(c,l,r)。

现在就只需要维护cnt(d,l,r)-cnt(c,l,r)的最大值就可以了。

这里有许多种维护方式,我说下我用的。

假设a[x1]=a[x2]=...a[xn]=d,那么对于[x1+1,x2-1]...[xn+1,n]这些区间,是没有d的,则我们需要维护的值就变为了-cnt(c,l,r),这里就相当于把连续的区间缩成了一个点。

为什么能缩点?我理解的就是选择的区间是一段连续的区间,缩点后的每个点,只要把当前的点加上的和为正(此时把当前连续的区间选完)就可以选择这一个点,否则就直接以下一个点为起点(当前区间就一个不选,之前已经保存了一个最大值了)。

所以对于一段连续的没有d的区间,要么选择连续的一段区间,要么直接不选,所以缩点后,用最大连续子段和的技巧就可以了~

代码如下:

#include <bits/stdc++.h>
using namespace std; const int N = 1e6 ;
int a[N];
int cnt[N];
int n,c,ans=-;
vector <int> pos[N];
int calc(int x){
if(x==c) return ;
vector <int> vec;
int len = pos[x].size();
if(len<=) return ;
vec.push_back(-cnt[pos[x][]-]);
vec.push_back();
for(int i=;i<len;i++){
vec.push_back(-cnt[pos[x][i]-]+cnt[pos[x][i-]]);
vec.push_back();
if(i==len-) vec.push_back(-cnt[n]+cnt[pos[x][i]]);
}
int tot = ,sum = ,l = vec.size();
for(int i=;i<l;i++){
sum+=vec[i];
if(sum>=tot){
tot=sum;
}else if(sum<) sum=;
}
return max(tot,sum);
}
int main(){
scanf("%d%d",&n,&c);
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
cnt[i]=cnt[i-]+(a[i]==c);
pos[a[i]].push_back(i);
}
for(int i=;i<=N-;i++){
ans=max(calc(i)+cnt[n],ans);
}
printf("%d",ans);
return ;
}
05-11 22:10