CodeForces 1303D Fill The Bag

Description

描述

你有一个大小为 $n$ 的背包和 $m$ 个大小为 $2$ 的非负整数次幂的物品,其中第 $i$ 个物品的大小为 $a_i$。

每次可以把一个物品划分成两个相同大小的物品。你需要恰好填满整个背包。

例如,当 $n = 10$ 且 $a = [1,1,32]$ 时,你可以把大小为 $32$ 的物品划分成两个大小为 $16$ 的物品,再把其中一个大小为 $16$ 的物品划分成两个大小为 $8$ 的物品,这样你就可以用两个大小为 $1$ 的物品和一个大小为 $8$ 的物品填满这个背包。

求出最小的划分物品的次数。

输入

第一行一个正整数 $T$,表示 $T$ 组数据。

每组数据的第一行为两个正整数 $n$($1\ le 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 = \sum\limits_{i=1}^{m} a_i$,显然,有解的充要条件是 $sum \ge n$,反之无解。

我们从 $2^0 \to 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 \to 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\n";
			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;
}

猜你喜欢

转载自www.cnblogs.com/syksykCCC/p/CF1303D.html
今日推荐