题意
把一个数分解成恰好 \(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
模拟即可。
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<<' ';
}
标签:Binary,元素,题解,Jamie,最小,max,序列,拆掉,字典
From: https://www.cnblogs.com/redacted-area/p/18389458