思路:
先取出二进制的每一位,判断总个数是不是小于等于k,如果大于k则不能构成。
通过观察可以发现,每一位的一个可以转换成下一位的两个,因为要使最大位尽可能小,所以如果这一位的所有的个数都可以转换成下一位那么就全部转换过去,如果不能就一个也不要转换,不然会导致字典序损失。
然后从最小的位开始转换,每次取出最小的位一个数,转换成下一位的两个,直到不能转换为止,这样就能保证字典序最大了,这个用优先队列维护下。
代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) int a[200]={0}; int main(){ ios::sync_with_stdio(false); cin.tie(0); ll n; ll k; cin>>n>>k; for(int i=0;i<=62;i++){ if(n&(1ll<<i))a[i+100]=1; } int cnt=0; for(int i=0;i<=62;i++){ if(a[i+100])cnt++; } if(cnt>k){ cout<<"No"<<endl; return 0; } else if(cnt==k){ cout<<"Yes"<<endl; for(int i=62;i>=0;i--)if(a[i+100])cout<<i<<‘ ‘; cout<<endl; return 0; } else{ ll t=k-cnt; while(true) { bool f=true; for(int i=62;i>=-100;i--){ if(a[i+100]){ if(t>=a[i+100]){ t-=a[i+100]; a[i+99]+=a[i+100]*2; a[i+100]=0; f=false; } break; } } if(f||t==0)break; } priority_queue<ll,vector<ll>,greater<ll> >q; for(int i=62;i>=-100;i--){ if(a[i+100]){ for(int j=0;j<a[i+100];j++)q.push((ll)i); } } while(t){ if(q.empty())break; ll now=q.top(); q.pop(); t--; q.push(now-1); q.push(now-1); } cout<<"Yes"<<endl; vector<ll>vc; while(!q.empty()){ vc.pb(q.top()); q.pop(); } for(int i=vc.size()-1;i>=0;i--)cout<<vc[i]<<‘ ‘; cout<<endl; } return 0; }