题解 CF1781F Bracket Insertion
神仙 DP 题,不愧是 tourist。
容易发现,括号序列一共有 种生成方式。
假如 (
为 ,)
为 ,那么一个序列合法的充要条件为:最小前缀和为 ,且以 结尾。
现在考虑维护这些前缀和。
如果我们在当前序列某一位后插入一个 ()
,且那一位的前缀和为 ,那么相当于把 替换成 。
同理可知,插入 )(
相当于把 替换成 。
定义 为,执行 次操作,初始前缀和为 的方案数。
那么显然有两种转移:
其中, 对应第一个 , 对应 和 , 对应第二个 。
由于每个部分的操作都是独立的,所以还要乘上组合数。
状态 ,转移枚举 ,总复杂度 ,无法接受。
我们考虑优化掉一个 。将与 有关的都提到前面来。
定义
那么转移方程最终可化简为:
最后输出 即可。
复杂度 。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 500 + 5, mod = 998244353;
ll n, p, c[N][N], f[N][N], g[N][N];
ll ksm(ll x, ll y) {
ll res = 1;
while (y) {
if (y & 1) res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> p; p = p * ksm(10000, mod - 2) % mod;
for (int i = 0; i <= n; ++i) c[i][0] = 1;
for (int i = 1; i <= n; ++i) for (int j = 1; j <= i; ++j) c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
for (int i = 0; i <= n; ++i) f[0][i] = g[0][i] = 1;
for (int i = 1; i <= n; ++i) {
for (int x = 0; x <= n; ++x) {
for (int j = 0; j < i; ++j) {
f[i][x] = (f[i][x] + (p * f[j][x + 1] + (1 - p + mod) * (x ? f[j][x - 1] : 0)) % mod * c[i - 1][j] % mod * g[i - j - 1][x] % mod) % mod;
}
for (int j = 0; j <= i; ++j) {
g[i][x] = (g[i][x] + c[i][j] * f[j][x] % mod * f[i - j][x] % mod) % mod;
}
}
}
ll ans = f[n][0];
for (int i = 1; i <= 2 * n; i += 2) ans = ans * ksm(i, mod - 2) % mod;
cout << ans << endl;
return 0;
}
本文作者:HQJ2007
本文链接:https://www.cnblogs.com/HQJ2007/p/17561302.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通