cf Educational Codeforces Round 55 E. Increasing Frequency

原题:
E. Increasing Frequency
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
You are given array a of length n. You can choose one segment [l,r] (1≤l≤r≤n) and integer value k (positive, negative or even zero) and change al,al+1,…,ar by k each (i.e. ai:=ai+k for each l≤i≤r).

What is the maximum possible number of elements with value c that can be obtained after one such operation?

Input
The first line contains two integers n and c (1≤n≤5⋅10 ^ 5, 1≤c≤5⋅10 ^ 5) — the length of array and the value c to obtain.

The second line contains n integers a1,a2,…,an (1≤ai≤5⋅10 ^ 5) — array a.

Output
Print one integer — the maximum possible number of elements with value c which can be obtained after performing operation described above.

Examples
input
6 9
9 9 9 9 9 9
output
6
input
3 2
6 2 6
output
2
Note
In the first example we can choose any segment and k=0. The array will stay same.

In the second example we can choose segment [1,3] and k=−4. The array will become [2,−2,2].

中文:

给你两个数n和c,然后给你一个长度为n的序列。你可以对这个序列中任意取一个区间,将这个区间中所有的数加上一个值,且只允许执行一次这样的操作。现在问你在执行完这样一个操作后,序列中最多会有多少个c值?

代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=5e5+1;
typedef pair<int,int> pii;

int n,c;
int a[maxn];
int sum[maxn];
int top[maxn];

int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>c)
    {
        memset(sum,0,sizeof(sum));
        memset(top,0,sizeof(top));
        int ans=INT_MIN;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            if(a[i]==c)
                sum[i]=sum[i-1]+1;
            else
                sum[i]=sum[i-1];
        }
        for(int i=1;i<=n;i++)
        {
            top[a[i]]=max(top[a[i]]+1,sum[i-1]+1);
            ans=max(ans,top[a[i]]+sum[n]-sum[i]);
        }
         cout<<ans<<endl;
    }
	return 0;
}

思路:

此题很锻炼思维,很棒…

题目中表示可以对任意一个区间大小对这个区间中的数加上任意一个值,这表示这个区间的值任意一个数字都可以变成c。

简单的思路是这样,首先设置sum,对序列从头到尾求值为c的累加和。那么,sum[i]表示区间[1,i]当中有多少个c值。

选取区间cnt[L,R],找到这个区间当中频率最高的数字,然后将这个数字变成c值,那么最后得到的最优结果就是
a n s = m a x ( a n s , s u m [ L − 1 ] + c n t [ L , R ] + s u m [ n ] − s u m [ R ] ) ans=max(ans,sum[L-1]+cnt[L,R]+sum[n]-sum[R]) ans=max(ans,sum[L1]+cnt[L,R]+sum[n]sum[R])

如果按照单纯的枚举区间来计算,时间复杂度不能承受。

所以要考虑一种O(nlogn)或者O(n)的算法,在codeforces的讨论区当中看到一个带证明的解释,有点小错误,还有一点说的挺模糊,但是说的很好,这里借用一下。(原文地址


考虑一个区间[L,R],想要得到的这个区间当中出现频率最高的数字,假设这个数字的频率是x,那么最终结果就是
c n t [ 1 ,   l   −   1 ,   c ]   +   x   +   c n t [ r   +   1 ,   n ,   c ] cnt[1, l - 1, c] + x + cnt[r + 1, n, c] cnt[1,l1,c]+x+cnt[r+1,n,c],其中 c n t [ L , R , X ] cnt[L,R,X] cnt[L,R,X]表示区间[L,R]当中值为X的个数。

我们可以在在遍历序列 a [ i ] a[i] a[i]的过程当中,每次以当前的下标 i i i值作为区间[L,R]中的R值,来进行计算。
证明如下:
假设我们找到的出现频率最高的数字是y,而且y≠a[i],那么我们可以得到的一个更好的区间[L,R’],这里R’<R
,此时cnt[R’,R,y]=0, y = a r ′ y=a_{r^{&#x27;}} y=ar,这里的意思是将区间右边界R向左移动,直到a[R+1]值等于y。

此时可知 cnt[R’+1,n,c]≥cnt[R+1,n,c] 而且 cnt[L,R’,c] = cnt[L,R,c] ,所以有如下

c n t [ 1 , L − 1 ,   c ]   +   c n t [ L ,   R ,   y ]   +   c n t [ R   + 1 , n , c ]   ≤   c n t [ 1 , L − 1 , c ]   +   c n t [ L ,   R ′ ,   y ]   +   c n t [ R ′ + 1 , n , c ] cnt[1,L-1, c] + cnt[L, R, y] + cnt[R +1,n,c] ≤ cnt[1,L-1,c] + cnt[L, R&#x27;, y] + cnt[R&#x27;+1,n,c] cnt[1,L1,c]+cnt[L,R,y]+cnt[R+1,n,c]cnt[1,L1,c]+cnt[L,R,y]+cnt[R+1,n,c]
(原文最后一个公式写成cnt[R + 1, n, c])

通过如上公式,可以得到这样一条规律,在枚举区间的时候,可以顺序遍历a[i]枚举将枚举的a[i]当做右区间,去计算a[i]值出现的遍历,那么左区间怎么确定呢? 证明中没有说清楚,其实左区间也是想证明中说的情况一样,将序列中从1到i第一次出现a[i]这个值作为左区间,如下图,a[i]=2,和a[i]第一次出现的位置作为判断区间。
在这里插入图片描述
这样可以使用状态转移方程来计算,设 f a i f_{a_i} fai表示区间[1,i]当中可以得到的c值最大的个数,每次枚举到a[i]时状态转移 f a i = m a x ( f a i + 1 ,   c n t [ 1 ,   i   −   1 ,   c ]   +   1 ) f_{a_i}=max(f_{a_i}+1, cnt[1, i - 1, c] + 1) fai=max(fai+1,cnt[1,i1,c]+1),表示取上次将a[i]值改成c得到的最大值加上当前a[i]改成c的值,和仅想下标i的a[i]的c值加上仅使用区间[1,i-1]当中出现的c值。
最后结果加上区间cnt[i+1,n,c]即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值