1. 程式人生 > >Educational Codeforces Round 55 (Rated for Div. 2):E. Increasing Frequency

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; }