一、题目
1、题目描述
2、输入输出
2.1输入
2.2输出
3、原题链接
二、解题报告
1、思路分析
如果 popcount(n) > k,那没得玩了,直接NO
考虑不够k的情况下,我们如何调整?
2^i = 2^{i - 1} + 2^{i - 1},因此我们发现可以将一个高位拆成两个低一位
那么我们肯定从高位开始拆,为了保证最大值尽可能小
拆的不能拆后,可能还差几位,此时最大值无法更小,于是为了保证字典序最小,我们考虑将最小的位拆位,这个操作理论要进行k次,实际根本达不到,最多来个几次就没了
计数和拆位我们可以用有序集合来维护
2、复杂度
时间复杂度: O(KlogK)空间复杂度:O(log[N])
3、代码详解
#include <bits/stdc++.h>
#include <ranges>
// #define DEBUG
using i64 = long long;
using u32 = unsigned;
using u64 = unsigned long long;
constexpr int inf32 = 1e9 + 7;
constexpr i64 inf64 = 1e18 + 7;
void solve() {
i64 n, k;
std::cin >> n >> k;
std::map<int, int> cnt;
int pcnt = 0;
for (int i = 63; ~i; -- i)
if (n >> i & 1)
++ cnt[i], ++ pcnt;
if (pcnt > k) {
std::cout << "No\n";
return;
}
for (int i = 63; i >= -63; -- i)
if (pcnt + cnt[i] <= k)
pcnt += cnt[i], cnt[i - 1] += cnt[i] * 2, cnt[i] = 0;
else
break;
std::multiset<int> st;
for (int i = 63; i >= -63; -- i)
for(int j = 0; j < cnt[i]; ++ j)
st.insert(i);
while (st.size() < k) {
int fi = *st.begin();
st.erase(st.begin());
st.insert(fi - 1), st.insert(fi - 1);
}
std::cout << "Yes\n";
for (auto it = st.rbegin(); it != st.rend(); ++ it)
std::cout << *it << ' ';
std::cout << '\n';
}
auto FIO = []{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
} ();
int main() {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int t = 1;
// std::cin >> t;
while (t --)
solve();
return 0;
}