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; }
重要的是自信,一旦有了自信,人就会赢得一切。