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;
}