有一个数组 H,表示你构造的数组 X 中每一个位置的值上界,下界都是 1。
通过 X 构造数组 P,表示左边最后一个大于它的数的下标,如果没有就是 -1。
问你能构造出多少种不同的 P。
发现数组长度只有
100
100
100,但是值域能到
1
e
5
1e5
1e5。
但是完全没有必要,因为你只是大小关系,你完全可以把超过数组长度的设成数组长度,顶多就是它们两两之间都不一样嘛。
然后你发现这个
P
P
P 有点像一棵树,每次
i
i
i 指向
P
i
P_i
Pi,最后会形成以
−
1
-1
−1 为根的一棵树。
那问的就是这棵树能有多少形态呗。
而且边之间你放到区间里面是不会有交叉的,因为如果有
x
,
y
,
z
,
w
x,y,z,w
x,y,z,w 使得
z
→
x
,
w
→
y
z\rightarrow x,w\rightarrow y
z→x,w→y,那
x
>
z
,
y
>
w
,
w
⩾
z
x>z,y>w,w\geqslant z
x>z,y>w,w⩾z。
那
y
>
w
⩾
z
y>w\geqslant z
y>w⩾z 即
y
>
z
y>z
y>z,那不应该
z
→
y
z\rightarrow y
z→y 嘛。
所以是不交叉的。
那每个子树都是一个区间,你其实可以试着区间 DP。
然后你安排值,为了给后面留位置你肯定是能放多小放多小。
f
l
,
r
,
k
f_{l,r,k}
fl,r,k 表示一棵树
l
,
r
l,r
l,r 区间,里面最大值是
k
k
k。
g
l
,
r
,
k
g_{l,r,k}
gl,r,k 则表示森林的答案。
f
l
,
r
,
k
=
g
l
+
1
,
r
,
k
−
1
f_{l,r,k}=g_{l+1,r,k-1}
fl,r,k=gl+1,r,k−1 放一个根连着儿子。
g
l
,
r
,
k
=
∑
m
i
d
=
l
r
−
1
∑
v
=
1
k
f
l
,
m
i
d
,
v
g
m
i
d
+
1
,
r
,
k
+
∑
m
i
d
=
l
r
−
1
[
a
m
i
d
+
1
⩾
k
]
∑
v
=
1
k
−
1
f
l
,
m
i
d
,
k
g
m
i
d
+
1
,
r
,
v
g_{l,r,k}=\sum\limits_{mid=l}^{r-1}\sum\limits_{v=1}^kf_{l,mid,v}g_{mid+1,r,k}+\sum\limits_{mid=l}^{r-1}[a_{mid+1}\geqslant k]\sum\limits_{v=1}^{k-1}f_{l,mid,k}g_{mid+1,r,v}
gl,r,k=mid=l∑r−1v=1∑kfl,mid,vgmid+1,r,k+mid=l∑r−1[amid+1⩾k]v=1∑k−1fl,mid,kgmid+1,r,v
一种是直接放进去,一种是抬高了后面再放进去。
还有一种区间 DP 法。
前面我们从小到大考虑,我们考虑从大到小。
f
l
,
r
,
k
f_{l,r,k}
fl,r,k 表示
l
,
r
l,r
l,r 区间来处理,除了自己本身的限制还有值域的全部上界
k
k
k。
然后考虑从右往左枚举子树,因为子树之间那个儿子的值是要递增的。
那枚举最右边的那个子树的那个儿子的编号
m
i
d
mid
mid,那右边
[
m
i
d
+
1
,
r
]
[mid+1,r]
[mid+1,r] 的值域就是
k
−
1
k-1
k−1,左边的就是
k
k
k。
两边方案乘起来,所以分的情况加起来即可。
#include
#include
#define ll long long
#define mo 1000000007
using namespace std;
const int N = 105;
int n, a[N];
ll f[N][N][N], g[N][N][N], fs[N][N][N], gs[N][N][N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]); a[i] = min(n, a[i]);
}
for (int len = 1; len <= n; len++)
for (int l = 1; l + len - 1 <= n; l++) {
int r = l + len - 1;
if (l == r) {
f[l][r][1] = g[l][r][1] = 1;
for (int i = 1; i <= n; i++) fs[l][r][i] = gs[l][r][i] = 1;
continue;
}
for (int i = 2; i <= a[l]; i++) f[l][r][i] = g[l + 1][r][i - 1];
for (int i = 1; i <= n; i++) {
g[l][r][i] = f[l][r][i];
for (int mid = l; mid < r; mid++) {
(g[l][r][i] += gs[l][mid][i] * f[mid + 1][r][i] % mo) %= mo;
if (i <= a[mid + 1]) (g[l][r][i] += g[l][mid][i] * fs[mid + 1][r][i - 1] % mo) %= mo;
}
}
for (int i = 1; i <= n; i++) fs[l][r][i] = (fs[l][r][i - 1] + f[l][r][i]) % mo, gs[l][r][i] = (gs[l][r][i - 1] + g[l][r][i]) % mo;
}
ll ans = 0;
for (int i = 1; i <= n; i++) (ans += g[1][n][i]) %= mo;
printf("%lld", ans);
return 0;
}
#include
#include
#define ll long long
#define mo 1000000007
using namespace std;
const int N = 105;
int n, a[N];
ll rem[N][N][N];
ll slove(int l, int r, int k) {
if (l > r) return 1;
if (!k) return 0;
if (l == r ) return 1;
if (rem[l][r][k] != -1) return rem[l][r][k];
rem[l][r][k] = 0;
for (int mid = l; mid <= r; mid++) {
int val = min(k, a[mid]);
(rem[l][r][k] += slove(l, mid - 1, val) * slove(mid + 1, r, val - 1) % mo) %= mo;
}
return rem[l][r][k];
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) for (int k = 1; k <= n; k++) rem[i][j][k] = -1;
printf("%lld", slove(1, n, n));
return 0;
}