本題包含多組數據。c++
第一行:一個整數\(T\),表示數據的個數。spa
對於每組數據:debug
第一行:兩個整數,\(N\)和\(K\)(含義如題目表述)。3d
接下來\(N\)行:每行一個字符串。code
對於\(100\%\)的數據,\(T ≤ 5,M ≤ 15\),字符串長度\(≤ 50\)。blog
若存在輸出\(YES\),不然輸出\(NO\)。字符串
5 3 3 ???r??? ??????? ??????? 3 4 ??????? ?????a? ??????? 3 3 ??????? ?a??j?? ????aa? 3 2 a?????? ??????? ??????? 3 2 ??????? ???a??? ????a??
914852 0 0 871234 67018
其實看到那麼小的數據範圍那解題方法也就只有狀壓或者容斥了。get
鑑於網上狀壓的題解過多,咱們來介紹一下容斥的寫法。input
很顯然,咱們對於一個狀態咱們要求出有多少種符合條件的串。it
怎麼寫呢?
對於當前狀態\(i\),長度爲\(len\),咱們逐位考慮,對於當前爲\(j\)。
對於每一個在當前狀態的串。
1.若出現有兩個串的\(Sx[j]!=?,Sy[j]!=?,Sx[j]!=Sy[j]\)則說明不存在符合條件的串。
2.若出現該位上全部串都爲\(?\),即對於全部\(k\in i\)都有\(S_k[j]=?\),則該位上隨便選,則方案數乘\(26\)。
3.不然總方案數不變。
而後,咱們發現若直接這樣寫,會出現重複的一部分咱們沒減掉。
就是會出現一個串在狀態\(i\)可行,但在狀態\(j\)也可行,因此咱們要減掉。
咱們倒着枚舉狀態,對於當前\(i\),\(i\)的子集一定會包含\(i\)的可行狀態,因此咱們只用把\(i\)的子集減去\(i\)的方案數便可。
最後,咱們把全部恰好有\(k\)個\(1\)的狀態的\(dp\)累加到答案上便可。
#include <bits/stdc++.h> using namespace std; #define int long long #define reg register #define clr(a,b) memset(a,b,sizeof a) #define Mod(x) (x>=mod)&&(x-=mod) #define abs(a) ((a)<0?-(a):(a)) #define debug(x) cerr<<#x<<"="<<x<<endl; #define debug2(x,y) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl; #define debug3(x,y,z) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl; #define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a) #define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a) #define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a) #define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i]) #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize(3,"Ofast","inline") inline int Read(void) { int res = 0, f = 1; char c; while (c = getchar(), c < 48 || c > 57)if (c == '-')f = 0; do res = (res << 3) + (res << 1) + (c ^ 48); while (c = getchar(), c >= 48 && c <= 57); return f ? res : -res; } template<class T>inline bool Min(T &a, T const&b) {return a > b ? a = b, 1 : 0;} template<class T>inline bool Max(T &a, T const&b) {return a < b ? a = b, 1 : 0;} const int N = 20, M = (1 << 16) + 5, mod = 1e6 + 3; bool MOP1; int dp[M], Sz[M]; char S[N << 2][N << 2]; inline void Solve(void) { int n = Read(), K = Read(), Ans = 0; ret(i, 0, n)scanf("%s", S[i] + 1); int len = strlen(S[1] + 1); clr(dp, 0); drep(i, (1 << n) - 1, 0) { if (Sz[i] < K)continue; int res = 1; rep(j, 1, len) { int flag = 1, pos = -1; ret(k, 0, n)if (i & 1 << k) { if (S[k][j] == '?')continue; if (pos == -1) { pos = S[k][j] - 'a'; continue; } if (S[k][j] - 'a' != pos) { flag = 0; break; } } if (flag && pos == -1)res *= 26, res %= mod; if (!flag)res = 0; } dp[i] += res, Mod(dp[i]); if (Sz[i] == K)Ans += dp[i], Mod(Ans); for (int j = i & (i - 1); j; j = i & (j - 1)) { dp[j] += mod - dp[i], Mod(dp[j]); } } printf("%lld\n", Ans); } bool MOP2; void _main(void) { int T = Read(); ret(i, 1, M)Sz[i] = Sz[i & (i - 1)] + 1; while (T--)Solve(); } signed main() { _main(); return 0; }