Codeforces Round #791 (Div. 2) E. Typical Party in Dorm
传送门
\(\texttt{Difficulty:2400}\)
题目大意
有一个长为 \(n(1\le n \le 1000)\) 的由前 \(17\) 个小写字母以及 \(?\) 组成的字符串 \(s\) ,接下来有 \(q(1\le q \le 2\cdot 10^5)\) 次询问,每次询问给出一个只包含前 \(17\) 个小写字母的字符集 \(t(1\le |t|\le 17)\) 。 \(?\) 可以填入 \(t\) 中的任意一个字符,求可以获得的字符串中所有回文子串的总数,对 \(998244353\) 取模。
思路
考虑离线来做,用二进制数来表示字符集,设 \(f_{l,r}\) 为 \(s[l:r]\) 这个串变成回文串所需要的最小字符集,\(g_{l,r}\) 为子串 \(s[l:r]\) 为回文串时,整个 \(s\) 中可以自由填写的 \(?\) 个数, \(cnt_{i,j}\) 为所有需要的最小字符集为 \(i\) 的子串,在查询所给定的字符集大小为 \(j\) 时对答案的贡献。
对于 \(f_{l,r}\) 和 \(g_{l,r}\) ,我们考虑 \(s[l]\) 和 \(s[r]\) 的情况,从 \(s[l+1:r-1]\) 向 \(s[l:r]\) 转移:
当 \(s[l] = s[r] \ne \space?\) 时,二者都没有变化,于是 \(f_{l,r}=f_{l+1,r-1}\) , \(g_{l,r}=g_{l+1,r-1}\)
当 \(s[l] = s[r] = \space?\) 时,\(f\) 没有变化,但两个 \(?\) 中会有一个受限于另外一个 \(?\) ,于是 \(f_{l,r}=f_{l+1,r-1}\) , \(g_{l,r}=g_{l+1,r-1} - 1\)
当 \(s[l] = \space?\) , \(s[r] \ne \space?\) 时,唯一的 \(?\) 只能够填入一个字符,于是 \(f_{l,r}=f_{l+1,r-1}|(1<<(s[r]-'a'))\) , \(g_{l,r}=g_{l+1,r-1} - 1\) 。 \(s[l] \ne ?\) , \(s[r] = ?\) 时类似
不能回文时令 \(f_{l,r}\) 为 \(-1\) ,并且不去更新 \(cnt\)。初始值令 \(f=0\), \(g=s\) 中 \(?\) 个数。
对于 \(cnt_{i,j}\) ,转移到每个子串 \(s[l:r]\) 时,对每一个 \(j\) ,我们让 \(cnt_{f_{l,r},j}+=j^{g_{l,r}}\) 即可。最后答案是 \(\sum_{i\subseteq t}cnt_{i,|t|}\) ,这个东西我们可以用子集和 \(dp\) 在 \(O(|t|^22^{|t|})\) 内预处理出来,最后可以 \(O(|t|)\) 回答每个询问, 总的复杂度为 \(O(|t|(n^2+|t|2^{|t|}+q))\) 。
代码
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define pb push_back
#define int LL
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
//#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 1010;
int N, Q;
string S;
int f[maxn][maxn], g[maxn][maxn], cnt[1 << 18][18], ans[1 << 18][18];
int qpow(int a, int x)
{
if (!a)
return 0;
int ans = 1LL;
while (x)
{
if (x & 1LL)
ans = ans * a % mod;
a = a * a % mod;
x >>= 1LL;
}
return ans;
}
void SOS()
{
for (int t = 0; t < 18; t++)
{
for (int i = 0; i < (1 << 18); i++)
ans[i][t] = cnt[i][t] % mod;
for (int i = 0; i < 18; i++)
{
for (int s = 0; s < (1 << 18); s++)
{
if ((s >> i) & 1)
ans[s][t] = (ans[s][t] + ans[s ^ (1LL << i)][t]) % mod;
}
}
}
}
void solve()
{
int cntx = 0;
for (int i = 0; i < N; i++)
cntx += S[i] == '?';
for (int l = 1; l <= N; l++)
{
for (int r = 1; r <= N; r++)
g[l][r] = cntx;
}
for (int i = 1; i <= N; i++)
{
for (int l = 1; l + i - 1 <= N; l++)
{
int r = l + i - 1;
if (i > 1)
{
f[l][r] = -1;
if (S[l - 1] != S[r - 1] && S[l - 1] != '?' && S[r - 1] != '?')
continue;
if (S[l - 1] == '?' && S[r - 1] == '?')
{
f[l][r] = f[l + 1][r - 1];
g[l][r] = g[l + 1][r - 1] - 1;
}
else if (S[l - 1] == '?' && S[r - 1] != '?')
{
f[l][r] = f[l + 1][r - 1] | (1LL << (S[r - 1] - 'a'));
g[l][r] = g[l + 1][r - 1] - 1;
}
else if (S[l - 1] != '?' && S[r - 1] == '?')
{
f[l][r] = f[l + 1][r - 1] | (1LL << (S[l - 1] - 'a'));
g[l][r] = g[l + 1][r - 1] - 1;
}
else
{
f[l][r] = f[l + 1][r - 1];
g[l][r] = g[l + 1][r - 1];
}
}
if (f[l][r] >= 0)
{
for (int j = 0; j <= 17; j++)
cnt[f[l][r]][j] = (cnt[f[l][r]][j] + qpow(j, g[l][r])) % mod;
}
}
}
SOS();
string str;
while (Q--)
{
cin >> str;
int sta = 0, len = str.length();
for (int i = 0; i < len; i++)
sta |= (1LL << (str[i] - 'a'));
cout << ans[sta][len] << endl;
}
}
signed main()
{
IOS;
cin >> N >> S >> Q;
solve();
return 0;
}