题解——CF1913C Game with Multiset
题意
判断能否取多集中若干个数使得它们的和等于 \(w\)。
解析
由于给的数都是 \(2\) 的幂,所以我们可以每次让 \(w\) 减去不大于它的在集合中存在的二进制位数最多的数,如果能减完那么就说明可以,否则说明不可能。
怎么保证这种策略是最优的?
假设 \(n=\lfloor log_2w\rfloor\),则有 \(2^n \le w\)。
如果不减 \(2^n\) 也能让 \(w\) 变成 \(0\),那么:
\(w=t_{n-1} \times 2^{n-1}+t_{n-2} \times 2^{n-2}+\dots+t_{0} \times 2^{0}\ge2^n\)
显然,在上式中必定有一些 \(t\) 大于 \(1\),我们不妨把它看成一个还没有处理进位的二进制数,第 \(i\) 位的数值为 \(t_i\)。
那么,在处理完进位后,这个二进制数必定有第 \(n\) 位大于等于 \(1\),这实际上是由若干个 \(2^i(0\le i<n)\) 组成的,于是我们就可以用 \(2^n\) 来替代这若干个 \(2^i\)。
故如果不减 \(2^n\) 能让 \(w\) 变成 \(0\),那么减去 \(2^n\) 也可以把 \(w\) 变成 \(0\),并且这样我们还省了若干个 \(2^i\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int cnt[30];
bool find(int x,int pos){ //x:当前 w 还剩多少,pos:将要减去的二进制数的位数
if(!x) return true;
if(pos < 0) return false;
int num = x >> pos; //计算 x 可以减去多少个2^pos
if(cnt[pos] >= num) x -= num << pos;
else x -= cnt[pos] << pos;
return find(x,pos - 1);
}
int main(){
int T;
cin>>T;
while(T--){
int t,x;
cin>>t>>x;
if(t == 1){
cnt[x]++;
}else{
cout<<(find(x,29) ? "YES" : "NO")<<endl;
}
}
return 0;
}

浙公网安备 33010602011771号