Fill The Bag CodeForces - 1303D(二进制+位运算)

You have a bag of size nn. Also you have mm boxes. The size of ii-th box is aiai, where each aiai is an integer non-negative power of two.

You can divide boxes into two parts of equal size. Your goal is to fill the bag completely.

For example, if n=10n=10 and a=[1,1,32]a=[1,1,32] then you have to divide the box of size 3232 into two parts of size 1616, and then divide the box of size 1616. So you can fill the bag with boxes of size 11, 11 and 88.

Calculate the minimum number of divisions required to fill the bag of size nn.

Input
The first line contains one integer tt (1≤t≤10001≤t≤1000) — the number of test cases.

The first line of each test case contains two integers nn and mm (1≤n≤1018,1≤m≤1051≤n≤1018,1≤m≤105) — the size of bag and the number of boxes, respectively.

The second line of each test case contains mm integers a1,a2,…,ama1,a2,…,am (1≤ai≤1091≤ai≤109) — the sizes of boxes. It is guaranteed that each aiai is a power of two.

It is also guaranteed that sum of all mm over all test cases does not exceed 105105.

Output
For each test case print one integer — the minimum number of divisions required to fill the bag of size nn (or −1−1, if it is impossible).

Example
Input
3
10 3
1 32 1
23 4
16 1 4 1
20 5
2 1 16 1 8
Output
2
-1
0
思路:这种二的幂次的题目,转化成二进制和位运算的就肯定没错了。我们把这m个数,在每一位的有多少个数是1计算出来,然后把n在哪一位上是1也给计算出来。我们从低到高遍历,如果这一位应该有数字但实际没有的话,就往高位去借。具体看代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int maxx=1e5+100;
ll a[maxx],b[70];
int xx[70],yy[70];
ll n;int m;

inline void init()
{
	for(int i=0;i<=60;i++) b[i]=(ll)pow(2,i);//1e18,60位就足够了
}
int main()
{
	init();
	int t;
	scanf("%d",&t);
	while(t--)
	{
		cin>>n>>m;
		ll sum=0;
		for(int i=1;i<=m;i++) cin>>a[i],sum+=a[i];
		if(sum<n) cout<<-1<<endl;
		else if(sum==n) cout<<0<<endl;
		else
		{
			memset(xx,0,sizeof(xx));
			memset(yy,0,sizeof(yy));
			for(int i=60;i>=0;i--) if((n&(ll)b[i])) xx[i]=1;
			for(int i=60;i>=0;i--)
			{
				for(int j=1;j<=m;j++) if((a[j]&(ll)b[i])) yy[i]++;
			}
			int ans=0;
			for(int i=0;i<=60;i++)
			{
				if(xx[i]&&yy[i]>0) yy[i+1]+=((yy[i]-1)>>1);//这一位用完之后,剩下的尽可能的扩展到下一位。
				else if(xx[i]&&yy[i]==0)
				{
					for(int j=i+1;j<=60;j++)
					{
						if(yy[j]>0)
						{
							yy[j]-=1;
							ans++;
							for(int k=j-1;k>i;k--)
							{
								yy[k]+=1;//对于[i+1,j-1]的位数来说,只增加了一个,剩下的那一个分裂为下一位了。
								ans++;
							}
							yy[i]+=2;//第i位增加两个。
							break; 
						}
					}
					if(yy[i]>0) yy[i+1]+=((yy[i]-1)>>1);//尽可能的留给下一位
				}
				else if(xx[i]==0&&yy[i]>0) yy[i+1]+=(yy[i]>>1);//尽可能的留给下一位
			}
			cout<<ans<<endl;
		}
	}
	return 0;
}

努力加油a啊,(o)/~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

starlet_kiss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值