loading...

最值分治:[hdu6854] Kcats

statement

\(n\) 排列 \(\{p_i\}\) 可以形成一个 \(n\) 长度的序列 \(\{a_i\}\),其中 \(a_i\) 表示将编号 \(j=1,2,\dots,i\)\(p_j\) 依次放入单调栈,所构成的单调栈大小。

\(114^{514}\) 年后,\(\{a_i\}\) 被重新发现,但其中的一些元素因年代久远已经看不清了。给定 \(\{a_i\}\),让你求能形成 \(\{a_i\}\)\(\{p_i\}\) 数量对 \(10^9+7\) 取模。看不清的 \(a_i\) 在输入中为 \(-1\),表示这一位 \(a_i\) 可以为任意值。

\(n \le 100\)

引入

前置知识:笛卡尔树。

单调栈的过程让人不禁想起笛卡尔树的建树。以 \(3,1,4,5,6,2\) 为例进行单调栈操作。

\(i\) 单调栈
\(1\) \(3\)
\(2\) \(1\) | \(3\) 被弹出
\(3\) \(1,4\)
\(4\) \(1,4,5\)
\(5\) \(1,4,5,6\)
\(6\) \(1,2\) | \(4,5,6\) 被弹出

注意到笛卡尔树单调栈建树过程:单调栈实际上维护的是根节点一直往右走形成的极右链。因此,单调栈的大小等于形成的笛卡尔树极右链上的节点数(深度)

为解决歧义,令 \(\{p_j|(1\le j \le i)\}\) 依次放入单调栈最终形成的笛卡尔树为 \(T_i\)

考虑 \(T_n\) 上的节点 \(i\)\(a_i\) 的关系。则 \(a_i = \text{The most left chain of }T_i\)\(T_i\) 中加入节点 \(i+1\),会将极右链上的若干节点 pop 出去,但在 \(i\) 节点的左边的祖先数量不变,因此可以推出 \(T_{i+1}\)\(a_i=\)\(i\) 节点的左边的祖先数量。可以归纳出 \(T_n\)\(\forall 1\le i\le n\)\(a_i=\)\(i\) 节点左边的祖先数量。问题得到了简化。

综上,题目的答案即是满足 \(^\dagger\)\(\forall 1\le i\le n,a_i \not = -1\)\(a_i=\)\(i\) 节点左边的祖先数量\(^\dagger\)\(T_n\) 数量。

solution

不难想到区间 \(\mathrm{DP}\) 维护 \(T_n\) 中的子树的方案总数,由于某些 \(a_i\) 不确定,再多加一维信息 \(d\) 表示子树的根节点左边的祖先数量为 \(d\)。有状转方程:

\[f_{l,r,d}= \sum _{k=l}^{r} \dbinom{r-l}{k-l}f_{l,k-1,d}f_{k+1,r,d+1} \]

维护的方案只关心 \([l,r]\) 子树中对每个节点放置的 \(p_i\) 的相对大小,因此转移很简单。因此,\(\dbinom{r-l}{k-l}\) 表示去除子树根节点后,从剩余 \(r-l\) 个点中选 \(k-l\) 个点放在左子树,其余放在柚子sheu。

#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio (0), cin.tie (0), cout.tie (0)
#define rep(i, l, r) for (int i = l, ttmpa = r; i <= ttmpa; i ++)
#define per(i, l, r) for (int i = r; i >= l; i --)
#define LL long long
#define PII pair<int,int>
using namespace std;
const int N = 105, mod = 1e9+7;
int n, a[N], C[N][N];
LL f[N][N][N];
inline void solve() {
	cin >> n;
	rep(i, 1, n) cin >> a[i];
	memset(f, 0, sizeof f);
	rep(len, 1, n) rep(r, len, n) {
			int l = r-len+1;
			rep(k, l, r) rep(d, (~a[k]?a[k]:1), (~a[k]?a[k]:n))
				(f[l][r][d] += (l<k?f[l][k-1][d]:1)*(k<r?f[k+1][r][d+1]:1)%mod*C[r-l][k-l])%=mod;
		}
	cout << f[1][n][1] << '\n';
}
int main () {
	FASTIO;
	n = 100;
	rep(i, 0, n) C[i][0] = 1;
	rep(i, 1, n) rep(j, 1, n) C[i][j] = (C[i-1][j]+C[i-1][j-1])%mod;
	int _;
	cin >> _;
	while (_--) solve();
	return 0;
}
posted @ 2025-02-10 17:19  goldspade  阅读(39)  评论(0)    收藏  举报