套路的考虑把括号串放到二维平面上考虑,记 ( 的权值为 ,) 的权值为 ,权值数组为 ,前缀和数组为 ,则括号串可以看作是一条过 点的折线。则此时路径最低点到起点的纵坐标高度差就是无法匹配的右括号数量,路径最低点到终点的纵坐标高度差就是无法匹配的左括号数量。记上面两个值分别为 ,则这个串的权值(即能匹配的括号数)就是 。
因此一个括号串可以用一个三元组 来表示,其中 表示无法匹配的右括号数量, 表示无法匹配的左括号数量, 表示已经确定可以匹配的权值。则显然原串中左括号数量是 ,右括号数量是 。
接下来考虑这些括号穿已经确定时,怎么排列才能让最终的权值最小。假设拼接后整条路径的最低点在第 个括号串中,对其他括号串而言,若其 则说明这个串整体在折线上向下走,应该放在第 个串的左边,否则这个串整体在折线上往上走,应该放在第 个串右边。此时第 个串对起点终点到全局最低点的高度差的和贡献了 的代价,其余串则贡献了 的代价。因此这样对答案的贡献就是 。为了让最终的权值尽量小,容易想到让 值最大的 作为中间的串 ,因此确定所有串后最小的权值就是 。记 即第 个串的左右括号数量,则权值还可以被表示为 的形式。
考虑分别计算两个部分的期望值。对长度为 的随机括号串,枚举其中的左括号数 可得 。因为所有 的总和为 ,因此对每个串直接枚举 的时间复杂度还是线性的。
然后考虑算 的值。这个部分相对比较困难。设 则有 。现在问题在于如何快速计算一个长度为 个随机括号串中 各个取值分布的概率情况。
考虑继续在折线图上考虑问题。把原来的折线路径整体向上平移 个单位,则此时路径从高度 出发,在高度 结束,整条路径任意时刻都不在 轴的下方且至少碰到过一次 轴。因此 就可以看作是平移后起点和终点高度的较小值。设 ,固定 相当于起点和终点中至少有一个高度为 且另一个高度至少为 。这个东西可以直接枚举另一个高度然后用反射容斥来算折线数量,省略中间的 dirty work,最后的答案就是:
- ,其中 为偶数。
- ,其中 为奇数(这个部分要乘以 是因为起点和终点的奇偶性不同需要算两遍)。
记上面的东西值为 。
因为总共存在 个长度为 的括号串,所以 。枚举 的过程中顺带维护 的前缀和即可线性得到当前字符串的所有 的值。
记 ,则因为各个括号串之间互相独立,因此 就是所有 的概率即 。因此 。具体实现过程中维护数组 的值,初始所有 的值都为 ,处理一个长度为 的串时枚举 维护 的前缀和,从而求出 然后乘入 中即可。
预处理阶乘,阶乘逆和二的幂的逆元即可做到线性求解问题。
:::success[Code]
namespace lowspeed_song {
int fac[N], inv[N], ifac[N], pwr[N], ipwr[N];
inline void init() {
fac[0] = inv[0] = ifac[0] = 1;
fac[1] = inv[1] = ifac[1] = 1;
for (int i = 2; i < N; ++i) {
fac[i] = fac[i - 1] * i % mod;
inv[i] = mod - inv[mod % i] * (mod / i) % mod;
ifac[i] = ifac[i - 1] * inv[i] % mod;
}
pwr[0] = ipwr[0] = 1;
for (int i = 1; i < N; ++i) {
pwr[i] = pwr[i - 1] * 2 % mod;
ipwr[i] = ipwr[i - 1] * i2 % mod;
}
}
inline int binom(int a, int b) {
if (b < 0 || a < b) return 0;
return fac[a] * ifac[b] % mod * ifac[a - b] % mod;
}
int f[N], a[N];
inline void sol() {
int n = 0, m, res = 0, mx = 0; cin >> m;
for (int i = 1; i <= m; ++i) cin >> a[i], n += a[i], mx = max(mx, a[i] / 2);
for (int i = 0; i < N; ++i) f[i] = 1;
for (int i = 1; i <= m; ++i) {
res = (res + a[i] * i2 % mod + mod - a[i] * binom(a[i] - 1, (a[i] - 1) / 2) % mod * ipwr[a[i]] % mod) % mod;
int pre = 0;
for (int j = 1; j <= a[i] / 2; ++j) {
pre = (pre + ((a[i] & 1) ? 2 * binom(a[i], a[i] / 2 - j + 1) % mod : binom(a[i] + 1, a[i] / 2 - j + 1))) % mod;
f[j] = f[j] * pre % mod * ipwr[a[i]] % mod;
}
} for (int i = 1; i <= mx; ++i) res = (res + mod - 1 + f[i]) % mod;
cout << res << '\n';
}
} // namespace lowspeed_song:::
喜欢的话,留下你的评论吧~