Educational Codeforces Round 55 (Rated for Div. 2):E. Increasing Frequency

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=-1;
vector <int> pos[N];
int calc(int x){
    if(x==c) return 0;
    vector <int> vec;
    int len = pos[x].size();
    if(len<=0) return 0;
    vec.push_back(-cnt[pos[x][0]-1]);
    vec.push_back(1);
    for(int i=1;i<len;i++){
        vec.push_back(-cnt[pos[x][i]-1]+cnt[pos[x][i-1]]);
        vec.push_back(1);
        if(i==len-1) vec.push_back(-cnt[n]+cnt[pos[x][i]]);
    }
    int tot = 0,sum = 0,l = vec.size();
    for(int i=0;i<l;i++){
        sum+=vec[i];
        if(sum>=tot){
            tot=sum;
        }else if(sum<0) sum=0;
    }
    return max(tot,sum);
}
int main(){
    scanf("%d%d",&n,&c);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        cnt[i]=cnt[i-1]+(a[i]==c);
        pos[a[i]].push_back(i);
    }
    for(int i=1;i<=N-5;i++){
        ans=max(calc(i)+cnt[n],ans);
    }
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/heyuhhh/p/10056246.html