Codeforces Contest 807 E Prairie Partition —— 二分

This way

题意:

一个数可以被分为 2 0 + 2 1 + 2 2 + . . . . + 2 k + r ( 0 &lt; r &lt; = 2 k + 1 ) 2^0+2^1+2^2+....+2^k+r(0&lt;r&lt;=2^{k+1}) 20+21+22+....+2k+r(0<r<=2k+1),现在给你一串分解之后的数,问你能够还原回去多少个数,输出所有不同种类的个数,比如1 1 1 2 2 2就可以还原回3,3,3或者4,5这两种,答案就是长度为3和长度为2

题解:

假设我们现在有这么一串序列:
在这里插入图片描述
那么还原的时候是不是就像一个类似单调栈的图啊,
在这里插入图片描述
最多的情况是不是就是这个样子,那么我们已知最多的情况为最底下的1的个数,那么我们二分最少的答案是不是就好了,每次减少的时候,可以用最外面的填进去啊,这样的话假设二分到的答案是4的话,我们就直接把一个1当做r即可,这样补充到可以划分的序列中就是最少的。
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int num[64],oth[64];
int check(int x)
{
    int res=num[0]-x;
    for(int i=1;i<=40;i++)
    {
        res+=oth[i];
        if(x>num[i])
        {
            res-=min(res,x-num[i]);
            x=num[i];
        }
        else
            res+=num[i]-x;
    }
    return res==0;
}
int main()
{
    int n;
    scanf("%d",&n);
    ll ret=1,x,cnt=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&x);
        while(ret<x)
            ret*=2,cnt++;
        if((ret^x)==0)
            num[cnt]++;
        else
            oth[cnt]++;
    }
    int l=1,r=num[0],ans=-1;
    while(r>=l)
    {
        int mid=l+r>>1;
        if(check(mid))
            r=mid-1,ans=mid;
        else
            l=mid+1;
    }
    if(ans==-1)
        return 0*printf("-1\n");
    for(int i=ans;i<=num[0];i++)
        printf("%d%c",i,i==num[0]?'\n':' ');
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值