https://codeforces.com/problemset/problem/1303/D
D. Fill The Bag
题目:
思路:
题意就是给你m个数,让你用这m个数拼成n,对于任意一个m,只要其大于1,那么就可以对齐进行拆分操作,这样就能得到两个 x/2
接下来我们想,题目所给的条件是每个 a[i] 都是 2 的x次幂,然后我们又要将 a[i] 拼成一个数,那我们就可以考虑使用二进制
那么对于任意一个数我们将其转化为二进制形式,如果该处是0,说明不需要考虑,否则说明只需要一个2的x次幂即可,所以我们用一个map来存储到底有多少个2的x次幂,这里我们可以直接存log(a[i]),因为到时候我们可以使用位运算来判断该位是0还是1,那对于第i位,我们只需要2的i次幂即可,即从map中寻找是否有i
接下来我们考虑如果没有对应的i呢?显然我们要找一个比i大数来拆分,假如我们需要,但是目前我们没有,那我们就要往上寻找,一直找到第一个map值大于1的b,即
那么我们一旦拆开就可以要拆成
一直到拆成,那么拆分的次数就是b-a了,同时我们发现,每次拆解都多了一个
,也就是说从a~b每一个都有了,那我们下一次是就可以直接到b的位置往后搜索了,特别的,如果当前有剩余的话,我们可以将下一位加上当前剩余的除以2,如1+1=2(对应2的1次幂),2+2=4(对应2的2次幂),那就能最大化利用了
同时注意,如果sum小于n的话,我们肯定不能构成,其他肯定可以,因为最后都能拆成1
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define ll long long
#define yes cout << "YES" << endl
#define no cout << "NO" << endl
void solve()
{
ll n, m;
cin >> n >> m;
vector<ll> a(m);
ll sum = 0;
map<int, int> c;
for (int i = 0; i < m; i++)
{
cin >> a[i];
sum += a[i];
c[log2(a[i])]++;
}
if (sum < n)
{
cout << -1 << endl;
return;
}
int i = 0, res = 0;
while (i < 60) {
if ((n >> i) & 1) { // 检查 n 的第 i 位是否为 1
if (c[i] > 0) {
c[i]--;
}
else {
while (i < 60 && c[i] == 0) {
i++;
res++;
}
c[i]--;
continue;
}
}
c[i + 1] += c[i] / 2;
i++;
}
cout << res << endl;
}
int main()
{
cin.tie(0)->sync_with_stdio(false);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}