CF946F

fi,j,k 表示在串 F(i) 的子序列中,s 中区间 [j,k] 作为子串的出现次数之和。

则有两种转移:一种是子序列完全包含在 F(i1)F(i2) 中,一种是子序列跨越两端。

先讨论后一种情况:fi,j,k=k1l=jfi1,j,l×fi2,l+1,k

然后讨论前一种情况,先讨论子序列全部位于 F(i1) 中的情况,发现当 k=n 时由于 [j,n] 已经不可能继续扩展了,所以 F(i2) 中的字符可以随便选。否则不能选,不然可能会算重。

这里说明一下为什么会算重:
如果选了的话,[j,k] 一定能扩展为 [j,k+1],这种情况属于子序列跨越两端,会在 fi,j,k+1=fi1,j,k×fi2,k+1,k+1 中被统计。

因此设 leni 表示 F(i) 的长度,则有:

fi,j,k={fi1,j,k×2leni2    k=nfi1,j,k                    kn

子序列全部位于 F(i2) 中的情况也同理:

fi,j,k={fi2,j,k×2leni1    j=1fi2,j,k                    j1

初值为 f0,i,i=[si=0],f1,i,i=[si=1]

最终答案为 fx,1,n

时间复杂度 O(xn3)

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 105, mod = 1e9 + 7;
int n, m;
char s[N];
int f[N][N][N];
int pw[N];

void add(int &a, int b) {
	a += b;
	if (a >= mod) a -= mod;
}

int main() {
	scanf("%d%d%s", &m, &n, s + 1);
	for (int i = 1; i <= m; ++i) f[s[i] - '0'][i][i] = 1;
	pw[0] = pw[1] = 2; for (int i = 2; i <= n; ++i) pw[i] = 1ll * pw[i - 1] * pw[i - 2] % mod;
	for (int i = 2; i <= n; ++i)
		for (int l = 1; l <= m; ++l)
			for (int r = l; r <= m; ++r) {
				add(f[i][l][r], 1ll * f[i - 1][l][r] * ((r == m) ? pw[i - 2] : 1) % mod);
				add(f[i][l][r], 1ll * f[i - 2][l][r] * ((l == 1) ? pw[i - 1] : 1) % mod);
				for (int k = l; k < r; ++k) add(f[i][l][r], 1ll * f[i - 1][l][k] * f[i - 2][k + 1][r] % mod);
			}
	printf("%d", f[n][1][m]);
	return 0;
}
posted @   Kobe303  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示