Educational Codeforces Round 55 (Rated for Div. 2):E. Increasing Frequency
阿新 • • 發佈:2018-12-02
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; }