1. 程式人生 > >Luogu P4996 咕咕咕

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