P2167

P2167 [SDOI2009] Bill的挑战 一种与众不同的做法。


暴力容斥。要求与刚好 \(k\) 个字符串匹配,考虑处理 \(f_s\) 表示与 \(s\) 集合中这些串都匹配的 \(T\) 的个数,则答案:

\[\sum_{s\in\text{U}\ \wedge\ |s|=k\ } \sum_{s \in \text{sub} \in \text{U}} -1^{|sub|-|s|}\cdot f_{sub} \]

其中 \(U=\{1, 2, 3, \cdots, n\}\)

\(f_s\) 可以直接暴力,遍历集合中每一个串,考虑每一个位置的方案。也可以记录下对于每个 \(s\) 的每一位的方案然后转移,会快一些。

单组时间复杂度 \(O(2^n\times len+2^k\times(^{k}_{n})\ )\ )\),大概就是非常跑不满的 \(O(2^n\times len+3^n)\)

上界大概只有 \(\text{2e7}\) 次运算,最大点 56ms 比较快吧还算。

// Problem: P2167
#include<iostream>
#include<stdio.h>
#include<string.h>
#define Int int
typedef long long ll;
#define typ int
int __Mst_p;
const int N = 16, L = 51, S = 32769, mod = 1e6 + 3;
typ read(){typ x=0,f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}return x*f;}
void print(typ x){if(x<0)putchar('-'),x=-x;if(x>9)print(x/10);putchar(x%10+48);}void print(typ x, char c){print(x);putchar(c);}
char gc(){char c=getchar();while(c==' '||c=='\n')c=getchar();return c;}

int t, n, k, len;
char a[N][L];
int g[S][L], f[S]; // g[s][i]:与 {s} 匹配的串,第 i 位的方案。

int __Med_p;
int main() {
    t = read();
    while(t--) {
        n = read(), k = read();
        for(int i = 1; i <= n; i++) scanf(" %s", a[i] + 1);
        len = strlen(a[1] + 1);
        for(int i = 1; i <= len; i++) g[0][i] = 26;
        for(int s = 1; s < (1 << n); s++) { // 2^n
            f[s] = 1;
            int lst = __builtin_ctz(s) + 1;
            for(int i = 1; i <= len; i++) { // len
                int tmp = g[s - (s & -s)][i];
                if(tmp == 26) {
                    if(a[lst][i] != '?') g[s][i] = a[lst][i] - 97;
                    else g[s][i] = 26;
                } else if(tmp >= 0) {
                    if(a[lst][i] == '?' || a[lst][i] - 97 == tmp) g[s][i] = tmp;
                    else g[s][i] = -1;
                } else g[s][i] = -1;
                f[s] = f[s] * (g[s][i] == 26 ? 26 : (g[s][i] == -1 ? 0 : 1)) % mod;
            }
        }
        int ans = 0;
        for(int s = 1; s < (1 << n); s++) {
            if(__builtin_popcount(s) != k) continue;
            int Cus = ((1 << n) - 1) ^ s;
            for(int set = Cus, sub; ; set = (set - 1) & Cus) {
                sub = set | s;
                ans = (ans + f[sub] * (__builtin_popcount(set) & 1 ? -1 : 1)) % mod;
                if(set == 0) break;
            }
        }
        print((ans + mod) % mod, '\n');
    }
    return 0;
}
posted @ 2024-01-13 10:28  CWzwz  阅读(5)  评论(0编辑  收藏  举报