【校内模拟】2048

题解

显然\(2048\)一定是若干个\(2^k(0\leq k \leq 11)\)凑出来的,我们不妨先将不是\(2^k\)的数去掉;

然后考虑求出剩下的数中有多少个子序列能凑出\(2048\)

我们可以考虑\(DP\)\(f[i][j]\)表示前\(i\)个数凑\(j\)的方案数,背包就可以,第一维可以滚动数组干掉

需要注意的一点是所有能凑出大于等于\(2048\)的数的序列都是合法序列,正确性是显然的(因为构成序列的最大的数也不超过\(2048\),所有的数都是\(2^k\)

不要忘了还有被去掉的数,加上他们依然可以凑出\(2048\),所以答案是\(f[cnt][2048]*(2^{n-cnt})\)

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define int long long
#define MOD 998244353
#define N 1000010
int n,a[N],m[2050],cnt,f[2050];
inline int qpow(int x,int k){
	int s=1;
	while(k){
		if(k&1) s=s*x%MOD;
		k>>=1;
		x=x*x%MOD;
	}
	return s;
}
#undef int
int main()
#define int long long
{
//	freopen("2048.in","r",stdin);
//	freopen("2048.out","w",stdout);
	memset(m,-1,sizeof(m));
	m[1]=0,m[2]=1,m[4]=2,m[8]=3,m[16]=4,m[32]=5,m[64]=6,m[128]=7,m[256]=8,m[512]=9,m[1024]=10,m[2048]=11;
	scanf("%lld",&n);
	int x;
	for(int i=1;i<=n;i++){
		scanf("%lld",&x);
		if(m[x]!=-1) a[++cnt]=x;
	}
	f[0]=1;
	for(int i=1;i<=cnt;i++){
		for(int j=a[i];j>=1;j--)
			f[2048]=(f[2048]+f[2048+j-a[i]])%MOD;
		for(int j=2048;j>=a[i];j--)
			f[j]=(f[j]+f[j-a[i]])%MOD;
	}
	printf("%lld\n",f[2048]*qpow(2,n-cnt)%MOD);
	return 0;
}
posted @ 2018-11-05 17:12  yjk  阅读(253)  评论(0编辑  收藏  举报