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;
}
posted @ 2022-05-17 12:05  Prgl  阅读(120)  评论(0编辑  收藏  举报