Description
描述
你有一个大小为 $n$ 的背包和 $m$ 个大小为 $2$ 的非负整数次幂的物品,其中第 $i$ 个物品的大小为 $a_i$。
每次可以把一个物品划分成两个相同大小的物品。你需要恰好填满整个背包。
例如,当 $n = 10$ 且 $a = [1,1,32]$ 时,你可以把大小为 $32$ 的物品划分成两个大小为 $16$ 的物品,再把其中一个大小为 $16$ 的物品划分成两个大小为 $8$ 的物品,这样你就可以用两个大小为 $1$ 的物品和一个大小为 $8$ 的物品填满这个背包。
求出最小的划分物品的次数。
输入
第一行一个正整数 $T$,表示 $T$ 组数据。
每组数据的第一行为两个正整数 $n$($1le n le 10^{18}$)和 $m$($1 le m le 10^5$)。
接下来为 $m$ 个正整数 $a_i$($1 le a_i le 10^9$)。
输出
每组数据一个数表示最小的划分物品的次数,如果无法实现输出 $-1$。
样例
输入
3
10 3
1 32 1
23 4
16 1 4 1
20 5
2 1 16 1 8输出
2
-1
0
Solution
我们可以按位贪心。
用 $c_i$ 表示 $a$ 数组中 $2^i$ 出现的个数,记 $sum = sumlimits_{i=1}^{m} a_i$,显然,有解的充要条件是 $sum ge n$,反之无解。
我们从 $2^0 o 2^{29}$ 贪心,比如现在在考虑 $2^i$ 这一位,如果 $n operatorname{and} 2^i=0$($n$ 不含这一位) ,那显然是没那个必要考虑的。如果是 $1$ 的话,那么用 $tmp$ 来表示用 $a$ 数组中 $2^0 sim 2^{i - 1}$ 能拼出来多少个 $2^i$。如果 $tmp ge 1$ 的话,直接 $tmp gets tmp - 1$ 就行了,表示消耗一个;如果 $tmp = 0$,那我们显然只能借助于 $2^i sim 2^{29}$ 来完成这个操作了,这时可以暴力的用 $j$ 从 $i o 29$ 枚举,找到第一个 $c_j ge 1$ 的,把 $c_j gets c_j - 1$,然后把 $c_i sim c_{j - 1}$ 都增加 $1$,同时 $ans$ 要累加上 $j - i$(从 $2^j$ 除到 $2^i$ 就是 $j - i$ 次),别忘了 $tmp$ 要变成 $0$。最后按照 $tmp$ 的定义,当我们考虑 $2^{i+1}$ 时,$2^i$ 显然是要累加进去的,所以 $tmp = frac{tmp + c_i}{2}$。
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int M = 100005; LL n, m, a[M], ans; int c[50]; int main() { ios::sync_with_stdio(false); int t; cin >> t; while(t--) { ans = 0; LL sum = 0; memset(c, 0, sizeof c); cin >> n >> m; for(int i = 1; i <= m; i++) cin >> a[i]; for(int i = 1; i <= m; i++) { int k = 0; sum += a[i]; while(a[i] > 1) k++, a[i] >>= 1; c[k]++; } if(sum < n) { cout << "-1 "; continue; } LL tmp = 0; for(int i = 0; i < 30; i++) { if(n >> i & 1) tmp--; if(tmp < 0) { int j; for(j = i; !c[j]; j++) c[j] = 1; c[j]--; ans += j - i; tmp = 0; } tmp = tmp + c[i] >> 1; } cout << ans << endl; } return 0; }