题目大意
给定一个字符串序列 \([t_1,t_2,\cdots ,t_m]\) ,定义 \(f([t_1,t_2,\cdots,t_m])\) 为至少是其中一个字符串 \(t_i\) 的子序列的字符串个数,其中 \(f([])=0\) 。
给定一个字符串序列 \([s_1,s_2,\cdots ,s_m]\),对每一个子集 \([s_{i_1}, s_{i_2}, \cdots, s_{i_k}]\) 求出 \(f({[s_{i_1}, s_{i_2}, \cdots, s_{i_k}]})\) 对 \(998244353\) 取模后的值。
输出 \(f({[s_{i_1}, s_{i_2}, \cdots, s_{i_k}]})\times k\times (i_1+i_2+\cdots+i_k)\) 的异或和(不取模)。
注意每个字符串 \(s_i\) 中的字母都是排好序的。
题解
设字符串 \(s\) 中字符 \(c\) 的个数为 \(\mathrm{cnt}(c)\),则该字符串的子序列的个数为 \(\prod_{c='a'}^{'z'}(\mathrm{cnt}(c)+1)\)。
对于同时是多个字符串的子序列的字符串的个数,只要对每个字符 \(c\) 将\(\mathrm{cnt}(c)\) 取 \(\min\) 加 \(1\) 再相乘即可。
接下来考虑容斥。设字符串集为 \(S\),设同时是 \(S\) 中所有字符串的子序列的字符串的个数为 \(g(S)\),则有
这个式子实际上是一个符号和奇偶校验码有关的子集和,直接用SOS DP或者说类似于FMT的方法直接算即可,时间复杂度 \(O(|\Sigma|n2^n)\),\(|\Sigma|\) 是字符集大小,本题中是 \(26\)。
\(x\) 的奇偶校验码可以用 __builtin_parity(x)
直接算,若 \(x\) 的二进制中有偶数个 \(1\),则返回 \(0\),否则返回 \(1\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL MOD = 998244353;
char buf[20010];
int f[1 << 23], a[26];
vector<int> v[25];
int n;
int main() {
scanf("%d", &n);
for (int i = 0;i < n;++i) {
scanf("%s", buf + 1);
v[i].resize(26);
for (int j = 1;buf[j];++j)
++v[i][buf[j] - 'a'];
}
for (int i = 1;i < (1 << n);++i) {
memset(a, 0x3f, sizeof(a));
for (int j = 0;j < n;++j) {
if (!(i & (1 << j))) continue;
for (int k = 0;k < 26;++k)
a[k] = min(a[k], v[j][k]);
}
f[i] = 1;
for (int k = 0;k < 26;++k)
f[i] = 1LL * f[i] * (a[k] + 1) % MOD;
}
for (int i = 0;i < n;++i) {
for (int j = 0;j < (1 << n);++j) {
int x = __builtin_parity(j);
if (j & (1 << i)) {
f[j] = (f[j] + -1 * f[j ^ (1 << i)]) % MOD;
if (f[j] < 0) f[j] += MOD;
}
}
}
LL ans = 0;
for (int i = 0;i < (1 << n);++i) {
if (!__builtin_parity(i)) f[i] = MOD - f[i];
int k = 0, x = 0;
for (int j = 0;j < n;++j)
if (i & (1 << j)) { ++k; x += j + 1; }
LL temp = 1LL * k * x * f[i];
ans ^= temp;
}
printf("%I64d\n", ans);
return 0;
}