[CF506E] Mr. Kitayuta's Gift

[题目链接]

https://codeforces.com/contest/506/problem/E

[题解]

首先假设 \(|s| + n\) 为偶数。

考虑朴素动态规划 , 令 \(dp_{i , l , r}\) 表示用了 \(i\) 个字符 , 字符串 \(S\) 还剩 \([l , r]\) 这段区间的方案数 , \(g_{i}\) 表示用了 \(i\) 个字符匹配了 \(S\) 的方案数。 转移不赘述了。

用矩阵乘法优化 , 可以得到一个 \(O(|S| ^ 6log|S|)\) 的做法。

注意到这个 \(DP\) 实质上实在一个有限状态自动机 (\(DFA\)) 上匹配的过程。 如图 :

考虑压缩自动机 , 注意到一条有 \(k\) 个红点的路径必然对应着 \(\rfloor \frac{|s| - k}{2} \rfloor\) 个绿点 , 同时最终连接终点的点一定是绿点。

对于两条红点个数相同的链 , 它们对答案的贡献与红点的具体位置无关 , 因为其自环数及点数都相同。 因此只有 \(O(|S|)\) 条本质不同的链。

\(h_{i , l , r}\) 表示从起点走到 \((l , r)\) 对应的节点的路径中 ,有多少条经过了 \(i\) 个红点 , 可以用记忆化搜索的方式求得 , 时间复杂度 \(O(|S| ^ 3)\)

现在我们只有了 \(O(|S|)\) 条本质不同的链 , 同时知道了每种链的条数 , 可以对每条链进行矩阵乘法 , 时间复杂度降为 \(O(|S| ^ 4log|S|)\)

但这样仍然是不行的 , 考虑建出一个 \(O(|S|)\) 个节点的自动机 (如图) :

这样就可以做到 \(O(|S| ^ 3log|S|)\) 了!

最后考虑奇数的情况 , 相比起偶数的情况 , 奇数的情况相当于限制了最后一步不能从一个 \(2\) 个字符的绿色节点转移而来 , 那么去除自动机终点的自环 , 求出这样的方案数减去即可。

但我们仍然不能通过此题。 考虑将节点编号 , 每条边都从编号小的节点连向编号大的。 那么这样转移矩阵就是一个上三角矩阵。 不用全部清零 , 复杂度被乘了一个 \(\frac{1}{6}\) 的常数。

问题得到了完美的解决。

[代码]

#include<bits/stdc++.h>

using namespace std;

#define rep(i , l , r) for (int i = (l); i < (r); ++i)

typedef long long LL;

const int MN = 207 , MM = 307 , mod = 10007;

int n , m , k;
char s[MN];
bool vis[MN][MN][MN];
int h[MN][MN][MN] , f[MM] , g[MM][MM] , F[MM] , G[MM][MM];

inline void inc(int &x , int y) {
	  x = x + y < mod ? x + y : x + y - mod;
}
inline void dec(int &x , int y) {
	  x = x - y >= 0 ? x - y : x - y + mod;
}
inline int ceil(int n) {
	  return (n >> 1) + (n & 1);
}
inline int H(int i , int l , int r) {
		if (i < 0) return 0;
		if (vis[i][l][r]) return h[i][l][r];
		vis[i][l][r] = 1;
		if (l == 1 && r == m) return h[i][l][r] = !i;
		if (l != 1 && s[l - 1] != s[r]) inc(h[i][l][r] , H(i - 1 , l - 1 , r));
		if (r != m && s[l] != s[r + 1]) inc(h[i][l][r] , H(i - 1 , l , r + 1));
		if (l != 1 && r != m && s[l - 1] == s[r + 1]) inc(h[i][l][r] , H(i , l - 1 , r + 1));
		return h[i][l][r];
}
inline void mul(int f[MM] , int g[MM][MM]) {
		int a[MM];
		memset(a , 0 , sizeof(a));
		for (int j = 1; j <= k; ++j)
		for (int i = 1; i <= k; ++i)
				inc(a[i] , 1ll * f[j] * g[j][i] % mod);
		for (int i = 1; i <= k; ++i) f[i] = a[i]; 
}
inline void mul(int g[MM][MM]) {
		int a[MM][MM];
		memset(a , 0 , sizeof(a));
		for (int i = 1; i <= k; ++i)
		for (int o = i; o <= k; ++o)
		for (int j = o; j <= k; ++j)
				inc(a[i][j] , 1ll * g[i][o] * g[o][j] % mod);
		for (int i = 1; i <= k; ++i)
		for (int j = 1; j <= k; ++j)
				g[i][j] = a[i][j]; 
}
inline void Exp(int o) {
		while (o) {
				if (o & 1) mul(f , g);
				mul(g); o >>= 1;
		}
} 

int main() {
		
		scanf("%s%d" , s + 1 , &n); m = strlen(s + 1); k = m + ceil(m);
		for (int i = 0; i < m; ++i) {
				int c = 0;
				for (int j = 1; j <= m; ++j) {
						inc(c , H(i , j , j));
						if (j != m && s[j] == s[j + 1]) inc(c , H(i , j , j + 1));
				}
				if (i) {
						g[i][k - ceil(m - i)] = c , g[i][i] = 24;
						if (i != 1) g[i - 1][i] = 1;
						else f[i] = 1;
				} else {
						f[m] = c , g[k][k] = 26;
						for (int j = m; j < k; ++j) g[j][j + 1] = 1 , g[j][j] = 25;
				}
		}
		if ((n + m) & 1) {
				for (int i = 1; i <= k; ++i) F[i] = f[i];
				for (int i = 1; i <= k; ++i)
				for (int j = 1; j <= k; ++j)
						G[i][j] = g[i][j];
		}
		Exp(ceil(n + m));
		if (!((n + m) & 1)) { printf("%d\n" , f[k]); return 0; }
		int ans = f[k];
		for (int i = 1; i <= k; ++i) f[i] = F[i];
		for (int i = 1; i <= k; ++i)
		for (int j = 1; j <= k; ++j)
				g[i][j] = G[i][j];
		for (int i = 0; i < m; ++i) {
				int c = 0;
				for (int j = 1; j <= m; ++j) 
						if (j != m && s[j] == s[j + 1]) inc(c , H(i , j , j + 1));
				if (i) g[i][k - ceil(m - i)] = c;
				else f[m] = c , g[k][k] = 0;
		}
		Exp(ceil(n + m));
		printf("%d\n" , (ans - f[k] + mod) % mod);
	  return 0;
}
posted @ 2021-01-24 17:35  evenbao  阅读(79)  评论(0编辑  收藏  举报