1. 程式人生 > 其它 >Codeforces Round #791 (Div. 2) E. Typical Party in Dorm

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