bzoj1879: [Sdoi2009]Bill的挑戰 狀壓dp
阿新 • • 發佈:2018-10-06
code 英文字母 str ostream () sdoi2009 mes 第一個字符 spl
題目傳送門
https://www.lydsy.com/JudgeOnline/problem.php?id=1879
題解
我們考慮用\(p[i][j]\)存所有字符串的信息,\(i\)表示字符串的第\(i\)個字符,\(j\)表示英文字母\(a-z\),\(p[i][j]\)用二進制存,例如\(p[1][0]=1100\)表示第一位可以為\(a\)的為第一個字符串和第二個字符串。
然後我們按字符串長度\(dp\),\(f[i][j]\)表示方案數,\(i\)表示字符串的第\(i\)個字符,\(j\)表示狀態,即前\(i\)位確定的情況下,\(j\)的二進制中為\(1\)的幾個數能組成一個相同字符串的方案數,然後轉移為
\[ f[i][j\&p[i][k]]=f[i][j\&p[i][k]]+f[i-1][j] \]
代碼
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int mod=1e6+3; const int maxn=50010; char s[20][60]; int vis[60][30]; int p[60][30]; int f[60][maxn]; int n,m,len,ans; int idx(char c){return c=='?'?26:c-'a';} int main(){ ios::sync_with_stdio(false); int T;cin>>T; while(T--){ ans=0; memset(f,0,sizeof(f)); memset(p,0,sizeof(p)); memset(vis,0,sizeof(vis)); cin>>n>>m; for(int i=1;i<=n;i++)cin>>(s[i]+1); if(m>n){cout<<0<<endl;continue;} len=strlen(s[1]+1); for(int i=1;i<=len;i++) for(int j=1;j<=n;j++){ int c=idx(s[j][i]); for(int k=0;k<26;k++){ if(c==k||c==26)p[i][k]=(p[i][k]<<1)+1; else p[i][k]=p[i][k]<<1; } } f[0][(1<<n)-1]=1; for(int i=1;i<=len;i++) for(int j=0;j<(1<<n);j++) if(f[i-1][j]) for(int k=0;k<26;k++) f[i][j&p[i][k]]=(f[i][j&p[i][k]]+f[i-1][j])%mod; for(int i=0;i<(1<<n);i++){ int num=__builtin_popcount(i); if(num==m)ans=1LL*(ans+f[len][i])%mod; } cout<<ans<<endl; } return 0; }
bzoj1879: [Sdoi2009]Bill的挑戰 狀壓dp