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;
}