题意:
一个数可以被分为 2 0 + 2 1 + 2 2 + . . . . + 2 k + r ( 0 < r < = 2 k + 1 ) 2^0+2^1+2^2+....+2^k+r(0<r<=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;
}