[BZOJ1879][SDOI2009]Bill的挑战

题目描述

img

Input

本题包含多组数据。

第一行:一个整数\(T\),表示数据的个数。

对于每组数据:

第一行:两个整数,\(N\)\(K\)(含义如题目表述)。

接下来\(N\)行:每行一个字符串。

对于\(100\%\)的数据,\(T ≤ 5,M ≤ 15\),字符串长度\(≤ 50\)

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

其实看到那么小的数据范围那解题方法也就只有状压或者容斥了。

鉴于网上状压的题解过多,我们来介绍一下容斥的写法。

很显然,我们对于一个状态我们要求出有多少种符合条件的串。

怎么写呢?


对于当前状态\(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;
}
posted @ 2019-10-07 16:37  dsjkafdsaf  阅读(123)  评论(0编辑  收藏  举报