CF1913C Game with Multiset 题解

【题目描述】

你有一个空的多重集,你需要处理若干下列询问:

  1. ADD $ x $:加入一个数值为 $ 2^x $ 的元素到该多重集。
  2. GET $ w $:判断是否存在一个该多重集的子集,使得这个子集的所有元素之和等于 $ w $。

$ 1≤m≤105,0≤x≤29,0≤w≤109 $

【思路】

操作一:ADD

首先,这个多重集里面只有二的幂次方的数,所以我们可以定义一个 $ sum $ 数组来表示这个多重集,$ sum_i $ 表示这个多重集里面有多少个数值为 $ 2^i $ 的元素。

这样子,操作一就很好实现了,直接 $ sum_x+1 $ 就行。

操作二:GET

我们发现,如果要让这个多重集有一个子集加起来等于 $ w $,我们需要满足:

  • 对于 $ w $ 的二进制表示法,如果倒数第 $ i $ 位为 $ 1 $(最后一位算第 $ 0 $ 位),则多重集之中一定存在一个 $ 2^{i} $。

但是现在我们的多重集可能会有多个相同的元素,他们可以“进位”成一个新元素,就像两个 $ 2^x $ 可以合并为一个 $ 2^{x+1} $ 一样。

这就难办了,我们不但需要考虑 $ w $ 需不需要当前的 $ 2^x $,还要关心“进位”。

我们可以这么想:

  1. 把这个多重集复制到一个新数组 $ sum1 $ 里去(以防后面的操作改变这个多重集原本结构)。
  2. 从低到高扫 $ w $ 的二进制位。
  3. 如果 $ w $ 倒数第 $ i $ 位为 $ 1 $(最后一位算第 $ 0 $ 位),则它需要一个值为 $ 2^{i} $ 的元素,$ sum1_i-1 $。
  4. 把其他所有当前值的元素进位(反正已经没用了)。

如果在取 $ w $ 的某一个二进制位置,发现没有这个值的元素了,就说明拼不出来。

否则就可以通过选取和“进位”拼出来。

【Code】

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

int sum[35],sum1[35];
int m,op,x,w;
bool Output; 

int main()
{
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		scanf("%d",&op);
		if(op==1){
			scanf("%d",&x);
			sum[x]++;
		}else{
			scanf("%d",&w);
			Output=false;
			for(int i=0;i<=31;i++) sum1[i]=sum[i]; //1.分离临时数组 
			for(int i=0;i<=31;i++){                //2.从小到大枚举二进制位 
				int flag=w&(1<<i);                 //3.检查当前位是否需要元素 
				if(flag){
					if(sum1[i]==0){ //需要,但是没有
						puts("NO");
						Output=true;
						break;
					}else{          //否则就用掉 
						sum1[i]--;
					}
				}
				sum1[i+1]+=sum1[i]/2;              //4.“进位”
			}
			if(!Output) puts("YES");
		}
	} 
	return 0;
}
posted @ 2024-02-24 17:22  Sundar_2022  阅读(14)  评论(0编辑  收藏  举报