题目链接
大意:给你一些2的幂次数,问你最少分解几次可以用一些2的幂次数得到n。
分解指的是: 2x−>2∗2x−1,x≥0
思路:把n按二进制拆分,从小到大枚举每一位1:先看低位能不能凑齐,否则从高位拆。
细节见代码:(写的比较复杂,理解是很好理解的)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
int t;
LL n;
int m, a[N];
LL res[88];
int fine(int x, LL num) {
if (!num)
return 1;
if (x < 0)
return 0;
if (res[x] >= num) {
return 1;
}
if (res[x] < num) {
return fine(x - 1, (num - res[x]) << 1);
}
}
void dele(int x, LL num) {
if (x < 0 || !num)
return;
if (res[x] >= num) {
res[x] -= num;
return;
}
dele(x - 1, (num - res[x]) << 1);
res[x] = 0;
return;
}
int ga(int x) {
int l = x;
for (; x <= 60; x++) {
if (res[x]) {
res[x]--;
for (int j = x - 1; j >= l; j--) {
res[j]++;
}
return x - l;
}
}
return -1;
}
int main() {
ios::sync_with_stdio(false);
for (cin >> t; t; t--) {
cin >> n >> m;
LL tot = 0;
memset(res, 0, sizeof res);
auto get = [&](int x) {
int cn = 0;
while (x) {
++cn;
x /= 2;
}
return cn - 1;
};
for (int i = 1; i <= m; i++) {
cin >> a[i], tot += a[i];
int x = get(a[i]);
res[x]++;
}
if (tot < n)
cout << -1 << '\n';
else if (tot == n)
cout << 0 << '\n';
else {
int sta = 0, tm = 0;
LL dis = n;
for (int j = 0; j <= 60; j++) {
if (dis & (1ll << j)) {
if (fine(j, 1)) {
dele(j, 1);
} else {
int x = ga(j);
if (x == -1) {
sta = 1;
break;
} else {
tm += x;
}
}
}
}
if (sta) {
cout << -1 << '\n';
} else {
cout << tm << '\n';
}
}
}
return 0;
}