https://codeforces.com/problemset/problem/1303/D
思路:
平时大到小贪心,这次小到大。一个变量记录此时的剩余总量。看能否给与当前位。>=必然是可以给与的。
假设当前位为2^pj,剩余总量的p^i(i<=j) ,如果存在==j的,肯定能给。假设全部i<j,剩余总量还能构造出这个2^j吗?肯定可以。
第一个都是二进制。
第二个 2^pi1+2^pi2+2^pi3+......>=2^pj; 对这个不等式不断/2;最后等式右边必然到0,等式左边必然>=0,如果不为0,剩下那部分就是多出来的,被消去的就是刚好构造成这个2^j的。
然后小到大,剩余总量对于当前位够了就够,不够从最近的大于这一位的往前找,分解了然后加上分解后数量。进行模拟。
Note:ai<=1e9,但是我开到61才过(调了好一会
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e5+1000;
typedef long long LL;
inline LL read(){LL x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
LL p[100],cnt[100];
map<LL,LL>map1;
LL a[maxn];
int main(void){
cin.tie(0);std::ios::sync_with_stdio(false);
p[0]=1;
for(LL i=1;i<=60;i++) p[i]=p[i-1]*2;
LL t;cin>>t;
while(t--){
LL n,m;cin>>n>>m;
map1.clear();
for(LL i=60;i>=0;i--){
cnt[i]=0;
if(n&p[i]) cnt[i]=1,n-=p[i];
}
for(LL i=1;i<=m;i++){
cin>>a[i];
for(LL j=0;j<=31;j++){
if(a[i]&p[j]) {map1[j]++;break;}
}
}
///低位往高位
bool flag=1;
LL sum=0;///剩余的总和
LL ans=0;
for(LL i=0;i<=60;i++){
sum+=(p[i])*map1[i];
if(cnt[i]){
if(sum>=p[i]){
sum-=p[i];
}
else{
for(LL j=i+1;j<=61;j++){///..?不是ai<=1e9
if(map1[j]>0){
map1[j]--;
ans+=j-i;
for(LL k=i;k<=j-1;k++) sum+=p[k];
break;
}
if(j==61) flag=0;
}
}
}
if(flag==0) break;
}
if(flag) cout<<ans<<"\n";
else cout<<"-1"<<"\n";
}
return 0;
}