P9460 众数 I

去洛谷看我的博客

思路

这道题可以改序列的 k k k 个数,问有多少个数字可以变成众数。

那么我们可以检查每个数字,来判断这个数字是否可以在序列被更改后作为众数。

从最优的思路出发,我们每次肯定是把数量最多的一个数字变成想要的数字,这样才能保证进行的操作最少,但是需要考虑的是原序列出现最多次数的数字可能不止一个,比如序列: 1 , 1 , 1 , 2 , 2 , 2 1,1,1,2,2,2 1,1,1,2,2,2,出现了三次的数字有两个,所以判断条件也绝对不是 n u m [ i ] + k > = m a x n − k num[i]+k>=maxn-k num[i]+k>=maxnk

而还需要注意的是,序列里不仅仅有出现次数最多的数字,还有次多的数字,所以想要 O ( 1 ) O(1) O(1),判断每个数字是否可行是几乎不可能的,我们只能从模拟的思路出发,每次将当前数字的数量加上目前最多数的个数,然后将目前最多数 − 1 -1 1(代表每个最多数都拿一个给i),再加上目前最大数原本的个数,直到操作次数被用完为止。

赛时代码(有点乱qwq)

#include<bits/stdc++.h>
using namespace std;
int n,k,a[1000005],num[1000005],nnum[1000005],maxn,ans,nn;
int main()
{
    cin>>n>>k;
    if(k>=n){cout<<"pigstd";return 0;}//小特判,不过应该不用的。
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        num[a[i]]++;//第一次桶排,统计出每个数字出现的次数
    }
    for(int i=1;i<=n;i++)
    {
        if(maxn<num[i]) maxn=num[i],nn=1;else if(maxn==num[i]) nn++;//统计最多次数和最多的有多少个数字
        nnum[num[i]]++;//统计每个出现次数的出现次数(桶排的桶排)
    }
    if(k<=nn)//分了个类,如果满足条件,则操作数用完最大次数要么不变,要么就是原来的减一,这样的话就和原来次多的数无关了
    {
    	if(k>=maxn-k/nn){cout<<"pigstd";return 0;}//判断原来有0个的数字可不可以变成众数,如果可以就代表所有数都可以变成众数。
    	for(int i=1;i<=n;i++)
    	{
        	if(num[i]+k>=maxn-k/nn) ans++;//判断这个数可不可以成为众数。
    	}
    	cout<<ans;return 0;
    }
    int tk=k,tmax=maxn,u,tn;//tk:临时k,tmax:临时最大值,u:当前数的数量,tn:当前最多数的个数
    for(int i=1;i<=n+1;i++)
    {
        tk=k,tmax=maxn,u=num[i],tn=nn;//更新最新的消息
        if(u>=tmax){ans++;continue;}//如果当前数量已经超过了最多数量的话,这个数是可以变成众数的。
        while(tk)
        {
            if(u>=tmax){ans++;if(num[i]==0){cout<<"pigstd";return 0;}break;}//判断同上,特判了0(好像没用,比赛时考虑不了那么多,随手就写上去了qwq
            if(tk<tn){//如果没办法把当前所有的最多数变成i。
                u+=tk;
                if(u>=tmax){ans++;if(num[i]==0){cout<<"pigstd";return 0;}break;}//立马判断一次,因为此时所有的操作数都用完了。
                else break;
            }
            u+=tn,tk-=tn,tmax--,tn+=nnum[tmax];//相当于每个当前最多数拿一个变成i后的各项数值,自己推一下就知道了。
            if(!tk)
            {
            	if(u>=tmax){ans++;if(num[i]==0){cout<<"pigstd";return 0;}}
                break;
            }//如果操作数是0了,就再判一次,因为下次没办法判了。
        }
    }
    if(ans==n+1){cout<<"pigstd";return 0;}//再特判,如果n+1(因为题中给了最大就是n)可以成为众数,那么所有数都可以
    cout<<ans;
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值