typewriter(容斥原理、组合)

题意

给定\(N\)个字符串,每个字符串是abcdefghijklmnopqrstuvwxyz的一个非空子序列(不一定连续)。

可以通过如下方式构造一系列字符串:选定一个上述字符串,随机选取其中\(L\)个字符(可以重复选择)组成一个字符串。

求总共可以构造出多少个字符串。

数据范围

\(1 \leq N \leq 18\)
\(1 \leq L \leq 10^9\)

思路

首先考虑使用一个字符串可以构造出多少个,显然是字母个数的\(L\)次方。

然后如果是使用两个字符串,将第一个字符串记为\(S_1\),第二个字符串记为\(S_2\)。那么答案为\(|S_1|^L + |S_2|^L - |S_1 \cap S_2|^L\)

因此,我们可以考虑使用容斥原理,使用单数个字符串为加,双数个字符串为减。

在代码实现过程中,__builtin_popcount()可以快速求出一个整数的二进制表示里面有多少个\(1\)

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <unordered_map>

using namespace std;

typedef long long ll;

const int N = 30, M = 50, mod = 998244353;

int n;
ll L;
char s[N][M];

ll qmi(ll a, ll b)
{
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return res;
}

int main()
{
    scanf("%d%lld", &n, &L);
    for(int i = 0; i < n; i ++) scanf("%s", s[i] + 1);
    ll ans = 0;
    for(int i = 1; i < (1 << n); i ++) {
        int cnt = __builtin_popcount(i);
        ll num = 0;
        unordered_map<char, int> mp;
        for(int j = 0; j < n; j ++) {
            if(i >> j & 1) {
                int len = strlen(s[j] + 1);
                for(int k = 1; k <= len; k ++) {
                    char ch = s[j][k];
                    mp[ch] ++;
                    if(mp[ch] == cnt) num ++;
                }
            }
        }
        ll tmp = qmi(num, L);
        if(cnt & 1) ans = (ans + tmp) % mod;
        else ans = (ans - tmp + mod) % mod;
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2022-06-07 16:29  pbc的成长之路  阅读(108)  评论(0编辑  收藏  举报