ICPC-南京2021 C题题解【Klee in Solitary Confinement】

博主为参加南京ICPC,和队友做相关题目,总结一道题的解题思路。题目是给定数组和数字k,对数组子数组加k,求最大众数。解题关键是用桶存数字数量,枚举元素作为众数,考虑如何增加其数量,还涉及找最大子区间和的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下个月就要去南京打ICPC了,今天和队友一起做一下这两年的南京ICPC题,这道题原本已经有正解的思路想法了,但是在实现过程没有想到快捷的方法,交cf上面出现了heap overflow的情况。赛后遗憾补题,在这里来总结一下这道题。

题意:

给定一个数组,和一个数字k,你可以选择对数组的一个子数组一致加上数字k,问选择操作或者不操作后能得到的最大众数是多少。

注意这里的数据范围,n是1e6,数组内的元素是-1e6到正1e6,又因为题目跟众数有关,所以很容易想到用一个桶将数字的数量存起来,这里就需要对输入的数字统一加上1e6,防止出现负数下标。

 然后不难想到一个性质,最终答案的众数一定是原本就已经出现在数组中的元素,因为如果是通过加上k后得到的新的众数,那么这些新众数在加之前的数量也等于加之前的数量。所以我们只需要枚举数组中的元素作为最终的众数,然后来考虑如何使当前枚举到的数的数量更多。

对于一个数字num,选择加与不加,只会影响num和num+k,假设枚举到了arr[i],单独只看数组内的arr[i]和arr[i]-k,我们相当于要选中一些区间,将里面的arr[i]移除,arr[i]-k变成arr[i],并且使arr[i]的数量最大。

我是考虑相当于对于每个众数,开一个vector存+1,-1,如果数组里出现arr[i]-k,就插一个+1,出现一个arr[i],就插入一个-1,选择区间统一加k就相当于选择一个区间,新出现的arr[i]数量相当于这个区间和,所以我们要选择一个区间和最大的子区间,将原来有的数量加上这个区间和,就是新产生的总数量,这个的最大值就是答案。

问题在于如何快速的找这个最大子区间。我自己做题时,找子区间我想去用双指针在那走,然后走来走去走乱了,不过我也有发现一个关键点,就是如果区间和是负数了,那直接清零,相当于不要了。所以可以直接扫一遍过去,用一个变量存一下区间和,如果负了直接清零,正的话就维护一下最大值。

而我补题时看的题解里面就更巧妙了,这个代码相当于同时处理多个数字的最大区间和,开一个数组cnt[maxn]维护当前枚举到的数的区间和,然后每更新一次,就判断一次答案是否用这个区间和来更新更大。就可以在扫一遍数组的同时完成所有数的维护。

//#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
using namespace std;
const int maxn=4e6+50;
int arr[maxn],num[maxn],dp[maxn];
int read(){
    int x=0,f=0,ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return f?-x:x;
}
signed main(){
    int n=read(),k=read();
    int ans=0;
    for(int i=1;i<=n;i++) arr[i]=read()+2e6,num[arr[i]]++;
    for(int i=1;i<=n;i++){
        dp[arr[i]]--;
        dp[arr[i]+k]++;
        if(dp[arr[i]]<0) dp[arr[i]]=0;
        ans=max(ans,num[arr[i]+k]+dp[arr[i]+k]);
    }
    cout<<ans<<endl;
    system("pause");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值