Luogu P4996 咕咕咕

题目传送门

题目名字真是十分契合Luogu的性质啊

IG NB


\(3^n\)的子集枚举不会……看了题解后只会正解

对于每个状态,其实对我们有用的只有这个状态中有多少个\(1\),而\(1\)的位置我们并不关心,因为具有相同个数个\(1\)的状态,它们出现的次数一定是相同的。

所以我们考虑用dp[i]表示有\(i\)\(1\)的方案数,因为我们可以一步一步的填\(1\),所以dp[i]=dp[i]+dp[i-j]*c[i][j],其中c[i][j]表示\(C^j_i\),即\(dp[i]=\sum\limits_{j=1}^{i} dp[i-j] \cdot C^j_i\)
有了上面的式子,我们预处理组合数和\(dp[i]\)就好了

一定要多取模,不然会炸。 十年OI一场空,忘掉取模见祖宗

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 998244353
#define LL long long
using namespace std;
LL c[30][30];
LL dp[30];
void init(){
	c[0][0]=1;
	for(int i=1;i<=20;i++) c[i][0]=1;
	for(int i=1;i<=20;i++)
	  for(int j=1;j<=i;j++)
	    c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
	dp[0]=1;
	for(int i=1;i<=20;i++)
	  for(int j=1;j<=i;j++)
		dp[i]=(dp[i]+(dp[i-j]*c[i][j])%mod)%mod;
}
int read(){
	char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	return c-48;
}
LL cnt,ans;
int main(){
	init();
	int n,m; cin>>n>>m;
	for(int i=1;i<=m;i++){
		cnt=0;
		for(int j=1;j<=n;j++)
		  if(read()) cnt++;
		LL k; cin>>k;
		ans=(ans+(((k*dp[cnt])%mod)*dp[n-cnt])%mod)%mod;
	}
	cout<<ans;
	return 0;
}
posted @ 2019-11-14 09:47  MorsLin  阅读(86)  评论(0编辑  收藏  举报