Codeforces 807E Prairie Partition 贪心思维+二分

来源:互联网 发布:淘宝装饰店铺教程 编辑:程序博客网 时间:2024/05/21 15:01

点击打开链接

题意:给出n个数,n<=1e5,a[i]<=1e12,问是否能将这n个数分成m条链,每条链为:1+2+4+..2^(k-1)+r  0<r<=2^k,找出所有的m?  
每条链为1...2^k-1 最多加上一个r,则m最大为1的出现次数 

假设能分成m条链 && m<c(1) 显然可以把多余的1从某条链中取出 形成m+1条,则只要二分找到最小成立的m
判定当链数为x,即加r次数<=x。

贪心:t从i=0开始,当2^i个数>x,则肯定要多出power[i]-x个R
当2^i个数<=x 即有x-power[i]条链最多只能到2^i,显然可以把过去的多余的R(R<=2^i)补到断链之后,之后就不在考虑这些断链.
更新最大添加数(当前可添加R的链数x)x=power[i]即可


 

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int N=2e3+20;ll n,a[N],num;int power[N];//pow[i] 2^i个数int between[N];//2^i~2^i+1之间的数个数bool check(int x){int t=power[0]-x;//多余的R for(int i=1;i<=63;i++)//2^i{if(x>=power[i]){t-=min(x-power[i],t);//多余的数添加到x-power[i]个断链上 x=power[i];//更新可以添加的R}elset+=power[i]-x;t+=between[i];// R<=2^i }return t<=x;}int main(){while(cin>>n){ll x;memset(between,0,sizeof(between));memset(power,0,sizeof(power));for(int i=0;i<n;i++){scanf("%I64d",&x);int cc=0;bool good=true;while(x>1){if(x&1)good=false;cc++;x>>=1;}if(good)power[cc]++;elsebetween[cc]++;} int ans=-1,l=1,r=power[0];while(l<=r){int mid=(l+r)>>1;if(check(mid)){ans=mid;r=mid-1;}elsel=mid+1;}if(ans==-1)cout<<ans;elsefor(int i=ans;i<=power[0];i++)cout<<i<<' ';cout<<endl;}return 0;}



0 0
原创粉丝点击