codeforces547B. Jamie and Binary Sequence (changed after round)

Jamie is preparing a Codeforces round. He has got an idea for a problem, but does not know how to solve it. Help him write a solution to the following problem:
Find k integers such that the sum of two to the power of each number equals to the number n and the largest integer in the answer is as small as possible. As there may be multiple answers, you are asked to output the lexicographically largest one.
To be more clear, consider all integer sequence with length k (a1, a2, …, ak) with . Give a value to each sequence. Among all sequence(s) that have the minimum y value, output the one that is the lexicographically largest.
For definitions of powers and lexicographical order see notes.

Input
The first line consists of two integers n and k (1 ≤ n ≤ 1018, 1 ≤ k ≤105) — the required sum and the length of the sequence.

Output
Output “No” (without quotes) in a single line if there does not exist such sequence. Otherwise, output “Yes” (without quotes) in the first line, and k numbers separated by space in the second line — the required sequence.
It is guaranteed that the integers in the answer sequence fit the range [ - 1018, 1018].

Examples
Input

23 5

Output

Yes
3 3 2 1 0 

Input

13 2

Output

No

Input

1 2

Output

Yes
-1 -1 

给出一个数n,求这样一个长度为k的数列{a}:

  1. sum(2ai) == n
  2. y=max{ai},使得尽可能的小

找出符合这两个条件的字典序最大序列并输出

因为对于任意整数x都有 2x=2x-1+2x-1 。所以只需要不断地将所有最高位向下拆分即可。若最高位拆分到d时长度超过k停止,那么d就是最小的最大值。
首先确定这个数的二进制表示,因为这样得到的序列时所有序列中长度最短的,也就是说可拆分次数最多的。如果最短序列都比给出的长度要长,那么输出 No。
之后按照上述方法确定最小最大值,并确定此时序列的长度。如果此时长度 lenS < k ,那么拆分最低位,因为拆分一个最低位会使序列长度+1,所以拆分最低位一定可以使序列增长至k,并保证字典序最大。


#include <stdio.h>
#include <climits>
#include <cstring>
#include <time.h>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <utility>
#include <vector>
#include <string>

#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return

#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;

bool appear[128];
queue<int> Q;
int main(){
    ios::sync_with_stdio(false);

    ll N;
    int k;
    cin>>N>>k;

    int num=0;
    while(N){
        if(N & 1){
            appear[num]=true;
            Q.push(num);
        }
        N=N>>1;
        num++;
    }

    int len=Q.size();
    if(len>k){
        cout<<"No"<<endl;
        re 0;
    }
    num--;
    int add=1;
    while(len+add<=k){
        len+=add;
        num--;
        if(num<0){
            add*=2;
        }else{
            add=add*2+(int)(appear[num]);
        }
    }

    cout<<"Yes"<<endl;
    if(num<0){
        rep(i,0,len-1){
            cout<<num<<" ";
        }
        num--;
        while(len<k){
            cout<<num<<" ";
            num--;
            len++;
        }
        cout<<num+1<<endl;
    }else{
        multiset<int> S;
        drep(i,num,0)
            if(appear[i])
                S.insert(i);
        int lenS=S.size();
        rep(i,0,len-lenS)
            S.insert(num);

        lenS=S.size();
        if(lenS==k){
            for(multiset<int>::reverse_iterator it = S.rbegin();it!=S.rend();it++)
                cout<<(*it)<<" ";
            cout<<endl;
        }else{
            int c=0;
            for(multiset<int>::reverse_iterator it = S.rbegin();it!=S.rend() && c<lenS-1;it++,c++)
                cout<<(*it)<<" ";
            int last=*(S.begin());
            last--;
            while(lenS<k){
                cout<<last<<" ";
                last--;
                lenS++;
            }
            last++;
            cout<<last<<endl;
        }
    }

    re 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值