CF946F
设 fi,j,k 表示在串 F(i) 的子序列中,s 中区间 [j,k] 作为子串的出现次数之和。
则有两种转移:一种是子序列完全包含在 F(i−1) 或 F(i−2) 中,一种是子序列跨越两端。
先讨论后一种情况:fi,j,k=k−1∑l=jfi−1,j,l×fi−2,l+1,k。
然后讨论前一种情况,先讨论子序列全部位于 F(i−1) 中的情况,发现当 k=n 时由于 [j,n] 已经不可能继续扩展了,所以 F(i−2) 中的字符可以随便选。否则不能选,不然可能会算重。
这里说明一下为什么会算重:
如果选了的话,[j,k] 一定能扩展为 [j,k+1],这种情况属于子序列跨越两端,会在 fi,j,k+1=fi−1,j,k×fi−2,k+1,k+1 中被统计。
因此设 leni 表示 F(i) 的长度,则有:
fi,j,k={fi−1,j,k×2leni−2 k=nfi−1,j,k k≠n
子序列全部位于 F(i−2) 中的情况也同理:
fi,j,k={fi−2,j,k×2leni−1 j=1fi−2,j,k j≠1
初值为 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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话