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]\)
對於 \(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}\)
當 \(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 ?\)
不能迴文時令 \(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;
}