「codeforces - 506E」 Mr. Kitayuta's Gift


description

给定字符串 \(s\),需要在 \(s\) 中添加 \(n\) 个字符(可以添在开头或末尾)使得 \(s\) 变回文。

求最后可以得到本质不同的回文串。

problem link。


solution

定义 \(f_{l,r,k}\) 表示回文串的最前/最后 \(k\) 个字符匹配上 \(s\)\(1\dots l-1\)\(r+1\dots|s|\) 的方案数。
转移讨论 \(s_l = s_r\) 是否成立即可。最后依回文串长度的奇偶性分类讨论。

很明显可以矩乘,但是状态数达到 \(O(|s|^2)\),显然过不了。

那么出题人的意思就是让我们压缩状态。但是我们懒得找性质,所以直接用 BM 硬刚即可。

求出前 \(A = 1000\) 项的全局答案(不知道准确上界,不过1000项应该够了),然后 BM + 多项式取模可以做到 \(O(|s|^2A + A^2\log n)\)。下面的代码附的就是这个做法。

附一个BM算法流程的博客。关于为什么构造出来的一定最短,可以尝试阅读 mx 的集训队论文(由于我水平有限,没有读懂,所以就不附证明了)


考虑合理合法地压缩状态。冷静分析一下,我们可以把状态和转移建图:

picture

一条路径的方案数事实上只与这个路径上的红点个数有关(绿点个数可以根据红点个数算出来)。

那么先 dp 一遍求出有多少路径包含 \(i\) 个红点,再用这 \(O(s)\) 状态新建一个转移图:

最后时间复杂度 \(O(s^3\log n)\),听说有些卡常,需要利用三角矩阵的特性把矩阵乘法的部分优化一下。

由于我懒癌,所以就不写这种做法了。


code

#include <map>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <algorithm>
using namespace std;

#define rep(i, x, n) for(int i=x;i<=n;i++)
#define per(i, x, n) for(int i=x;i>=n;i--)

const int MOD = 10007;
const int M = 1000;
const int N = 2*M;

inline int add(int x, int y) {x += y; return x >= MOD ? x - MOD : x;}
inline int sub(int x, int y) {x -= y; return x < 0 ? x + MOD : x;}
inline int mul(int x, int y) {return (int) (1LL * x * y % MOD);}
int mpow(int b, int p) {
	int r; for(r=1;p;p>>=1,b=mul(b,b))
		if( p & 1 ) r = mul(r, b);
	return r;
}

namespace linear_seq{

int t1[N + 5], t2[N + 5];
void pmul_mod(int *A, int *B, int *C, int *M, int n) {
	rep(i, 0, n - 1) t1[i] = A[i], t2[i] = B[i];
	per(i, 2*(n - 1), 0) C[i] = 0;
	rep(i, 0, n - 1) if( t1[i] )
		rep(j, 0, n - 1) C[i + j] = add(C[i + j], mul(t1[i], t2[j]));
	per(i, 2*(n - 1), n) if( C[i] )
		per(j, n, 0) C[i - j] = sub(C[i - j], mul(C[i], M[n - j]));
}

int b[N + 5], md[N + 5], bk;

void debug(int *A, int n) {
	rep(i, 1, bk) printf("%d%c", b[i], (i == bk) ? '\n' : ' ');
	rep(i, bk + 1, n) {
		int d = A[i];
		rep(j, 1, bk) d = sub(d, mul(b[j], A[i - j]));
		assert(d == 0);
	}
}

int r[N + 5], c[N + 5], ck, rk, iv, nw;
void update(int x, int d) {
	if( nw == -1 || ck - x < rk - nw ) {
		rk = ck, nw = x, iv = mpow(d, MOD - 2);
		rep(i, 1, rk) r[i] = c[i];
	}
}

int f[N + 5], g[N + 5];
int get(int *A, int n, int m) {
//	if( m <= n ) return A[m];
	
	rk = bk = 0, nw = -1;
	rep(i, 1, n) {
		int d = A[i];
		rep(j, 1, bk) d = sub(d, mul(b[j], A[i - j]));
		if( d ) {
			rep(j, 1, bk) c[j] = b[j]; ck = bk;
			if( nw == -1 ) {
				bk = i; rep(j, 1, bk) b[j] = 0; 
			} else {
				int t = i - nw + rk;
				rep(j, bk + 1, t) b[j] = 0;
				bk = max(bk, t);
				
				int coef = mul(d, iv), p = i - nw;
				b[p] = add(b[p], coef);
				rep(j, 1, rk) b[p + j] = sub(b[p + j], mul(coef, r[j]));
			}
			update(i, d);
		}
	}
	
//	debug(A, n);
	
	rep(i, 1, bk) md[bk - i] = sub(0, b[i]); md[bk] = 1;
	f[1] = 1, g[0] = 1;
	for(;m;m>>=1,pmul_mod(f, f, f, md, bk))
		if( m & 1 ) pmul_mod(g, f, g, md, bk);
	
	int ret = 0;
	rep(i, 0, bk - 1) ret = add(ret, mul(g[i], A[i + 1]));
	return ret;
}

}

int f[2][M + 5][M + 5], a[M + 5];
char s[M + 5]; int n, len;
int main() {
	scanf("%s%d", s + 1, &n), len = strlen(s + 1), n += len;
	
	int res = 0; f[0][1][len] = 1;
	rep(i, 1, M) {
		rep(j, 1, len) rep(k, j, len)
			f[1][j][k] = f[0][j][k], f[0][j][k] = 0;
		res = mul(res, 26);
		rep(j, 1, len) {
			res = add(res, f[1][j][j]);
			f[0][j][j] = add(f[0][j][j], mul(f[1][j][j], 25));
			rep(k, j + 1, len) {
				if( s[j] == s[k] ) {
					f[0][j][k] = add(f[0][j][k], mul(25, f[1][j][k]));
					if( j + 1 == k ) res = add(res, f[1][j][k]);
					else f[0][j + 1][k - 1] = add(f[0][j + 1][k - 1], f[1][j][k]);
				} else {
					f[0][j][k] = add(f[0][j][k], mul(24, f[1][j][k]));
					f[0][j + 1][k] = add(f[0][j + 1][k], f[1][j][k]);
					f[0][j][k - 1] = add(f[0][j][k - 1], f[1][j][k]);
				}
			}
		}
		
		if( n & 1 ) {
			a[i] = mul(res, 26);
			rep(p, 1, len) a[i] = add(a[i], f[0][p][p]);
		} else a[i] = res;
	}
	
	printf("%d\n", linear_seq::get(a, M, n / 2 - 1));
}

details

突然感觉到了BM是多么好用的一个东西。

posted @ 2020-07-17 21:31  Tiw_Air_OAO  阅读(14)  评论(0编辑  收藏