W
e
l
c
o
m
e
: )

题解:CF916B Jamie and Binary Sequence (changed after round)

题意

把一个数分解成恰好 \(k\)\(2^{a_i}\) 次方的和,可以重复,要求保证最大的 \(a_i\) 要尽可能的小时,\(a\) 的字典序尽可能大,输出序列 \(a\)

分析

首先我们借助二进制拆出一个满足 \(n=\sum 2^{a_i}\) 序列 \(a\),满足 \(a\) 的长度最小。

若此时 \(a\) 的长度大于 \(k\),显然无解。

因为 \(2^m=2^{m-1}+2^{m-1}\),我们每次取出序列中最大的元素 \(a_i\),然后向序列中加入两个 \(a_i-1\) 直到序列的长度等于 \(k\)

因为我们每次拆出的元素均为序列中的最大值,显然能保证此时 \(\max a_i\) 是最小的。

模拟上述过程,然后得到 WA on #5。


\(k=5\),而某一步得到的 \(a=\{4, 4, 2, 1\}\) 这种情况就能 hack 掉这种做法。

因为此时拆掉最大元素 \(4\) 得到的序列 \(a=\{4,3,3,2,1\}\) 的字典序小于拆掉最小元素 \(1\) 得到的序列 \(a=\{4, 4, 2, 0, 0\}\)

发现如果当前的 \(\max a_i\) 等于答案中的 \(\max a_i\) 时,拆掉最小元素的方案会使字典序大于拆掉最小元素的方案。

使用 set 模拟即可。

AC 记录

Code

#include<bits/stdc++.h>
using namespace std;

multiset<int, greater<int>> s, s0;

int main()
{
    int64_t n, k;
    cin>>n>>k;
    for(int i=0;i<63;i++) 
        if(n&(1ll<<i)) s.emplace(i);  
    if(s.size()>k) return cout<<"No", 0;
    cout<<"Yes\n";
    s0=s;
    while(s.size()<k)
    {
        int p=*s.begin();
        s.erase(s.begin());
        s.emplace(p-1);
        s.emplace(p-1);
    }
    int mx=*s.begin();
    while(s0.size()<k)
    {
        int p=*s0.begin();
        if(p==mx) break;
        s0.erase(s0.begin());
        s0.emplace(p-1);
        s0.emplace(p-1);
    }
    while(s0.size()<k)
    {
        int p=*s0.rbegin();
        s0.erase(prev(s0.end()));
        s0.emplace(p-1);
        s0.emplace(p-1);
    }
    for(auto i:s0) cout<<i<<' ';
}
posted @ 2024-08-30 20:41  Jimmy-LEEE  阅读(16)  评论(0)    收藏  举报