codeforces 1198A/round #576 Div2 C题解

MP3

题目连接:https://codeforc.es/contest/1199/problem/C

题意

  有一个文本,里面有很多数字 ai ,但是你的内存只有 I byte =8I bit,每一类不同的数字占一个bit,你每次只能对其中一个数字进行操作,增加数字或者减少数字,最终做到所有的数字在某个范围内[L,R],满足 k=log2(K) 向上取整,K为当前的区间长度,nk<=8*I(即这段连续的区间能被你的内存装下,n为数字的个数),要求用最少的操作次数,满足有足够的内存装下这段这段数字。

输入

  两个数 n ,I 表示数字的个数,I 为内存大小( 1<=n<=400000,1<=I<=1e8 ),第二行输入n个数字ai,( 0<=ai<=1e9 )

输出

  最小的操作次数

思路

  离散化+前缀和+尺取法
  对题目进行分析,所需要的区间是连续的区间[L,R],另外就是要求最少的操作次数,每次只能是一个数字,要所有的数字都在区间内,即不断地调整上限和下限,但是需要注意有相同的数字出现。所以不妨逆向思考,我们只要让某一段区间的数达到最大,其他的数直接调整进来,这保证了操作次数最少,最终答案就是总数-区间最大数字的数目。需要提醒,使一个数字增大或者减少并不限制增大多少或者减少多少。
  考虑数据范围,为了对每一类数计数,用到了离散化,重新开一个数组记录映射之后得到每一类数字的数量。
  为了快速得到某个区间的数字之和,用前缀和提前处理,最后从左到右尺取一遍,不断维护区间最大值。

代码

#include<bits/stdc++.h>
using namespace std;
#define maxn 400005
#define maxm 8
#define inf 2e9
#define ll long long

map<ll,ll>M;
ll num[maxn],cnt;
ll sum[maxn];
int main()
{
    ll n,I;
    scanf("%lld%lld",&n,&I);
    I*=8;
    for(ll i=1; i<=n; i++)
    {
        ll x;
        scanf("%lld",&x);
        M[x]++;
    }
    map<ll,ll>::iterator p1=M.begin();
    for(;p1!=M.end();p1++)
        num[++cnt]=(*p1).second;
    if(ceil(log2(cnt))*n<I)
    {
        printf("0");
        return 0;
    }
    for(int i=1;i<=cnt+1;i++)
        sum[i]=sum[i-1]+num[i];

    ll ans=0,Max=0;
    ll left=1,right=1;
    while(right<=cnt)
    {
        ll len=ceil(log2(right-left+1))*n;
        if(len<=I)
            right++;
        else
        {
            Max=max(Max,sum[right-1]-sum[left-1]);
            left++;
        }
    }
    Max=max(Max,sum[right]-sum[left-1]);
    printf("%lld",sum[cnt]-Max);
    return 0;
}
发布了41 篇原创文章 · 获赞 2 · 访问量 1267

猜你喜欢

转载自blog.csdn.net/qq_41418281/article/details/100017012