[BZOJ1879][SDOI2009]Bill的挑戰

題目描述

img

Input

本題包含多組數據。c++

第一行:一個整數\(T\),表示數據的個數。spa

對於每組數據:debug

第一行:兩個整數,\(N\)\(K\)(含義如題目表述)。3d

接下來\(N\)行:每行一個字符串。code

對於\(100\%\)的數據,\(T ≤ 5,M ≤ 15\),字符串長度\(≤ 50\)blog

Output

若存在輸出\(YES\),不然輸出\(NO\)字符串

Sample Input

5
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

其實看到那麼小的數據範圍那解題方法也就只有狀壓或者容斥了。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;
}
相關文章
相關標籤/搜索