题解——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;
}
posted @ 2023-12-19 12:40  yutar  阅读(58)  评论(0)    收藏  举报