最值分治:[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\)。有状转方程:
维护的方案只关心 \([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;
}

                
            
        
浙公网安备 33010602011771号