[BZOJ1879] [Sdoi2009]Bill的挑战
Description
Input
本题包含多组数据。
第一行:一个整数T,表示数据的个数。
对于每组数据:
第一行:两个整数,N和K(含义如题目表述)。
接下来N行:每行一个字符串。
T ≤ 5,M ≤ 15,字符串长度≤ 50。
Output
如题
Sample Input
5
3 3
???r???
???????
???????
3 4
???????
?????a?
???????
3 3
???????
?a??j??
????aa?
3 2
a??????
???????
???????
3 2
???????
???a???
????a??
3 3
???r???
???????
???????
3 4
???????
?????a?
???????
3 3
???????
?a??j??
????aa?
3 2
a??????
???????
???????
3 2
???????
???a???
????a??
Sample Output
914852
0
0
871234
67018
容易想到设$\large f[i][s]$表示正在填第i为,匹配的状态为s的方案数。
然后转移就很明显了,预处理出来每一位填每一个字母可以匹配的集合,然后可以快速转移。
这样做Luogu上AC了,但Bzoj的laji评测机过不去, 所以需要点优化。
首先小于k的状态不用转移,因为再怎么转移也不会让匹配成功的数目增多。
还有如果$\large f[i][s]$是0,那么就不用它转移, 这样452msAC
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <bitset> #include <ctime> using namespace std; #define reg register #define mod 1000003 int T; int n, m; char S[16][55]; int f[55][1<<15]; int g[55][500]; int bin[16]; int ans; int main() { bin[0] = 1;for(reg int i = 1 ; i <= 15 ; i ++) bin[i] = bin[i-1] << 1; scanf("%d", &T); while(T--) { memset(f, 0, sizeof f); memset(g, 0, sizeof g); scanf("%d%d", &n, &m); for (reg int i = 1 ; i <= n ; i ++) scanf("%s", S[i] + 1); int len = strlen(S[1] + 1); for (reg int i = 1 ; i <= len ; i ++) for (reg int k = 1 ; k <= n ; k ++) if (S[k][i] == '?') for (reg int j = 'a' ; j <= 'z' ; j ++)g[i][j] |= bin[k-1]; else g[i][S[k][i]] |= bin[k-1]; f[0][bin[n]-1] = 1; for (reg int i = 0 ; i < len ; i ++) { for (reg int s = 0 ; s <= bin[n] - 1 ; s ++) { if (!f[i][s]) continue; bitset <17> bit = s; if (bit.count() < m) continue; for (reg int j = 'a' ; j <= 'z' ; j ++) { int sit = s & g[i+1][j]; f[i+1][sit] = (f[i+1][sit] + f[i][s]) % mod; } } } ans = 0; for (reg int s = 0 ; s <= bin[n] - 1 ; s++) { bitset <17> bit = s; if (bit.count() == m) ans = (ans + f[len][s]) % mod; } printf("%d\n", ans); } return 0; }