Visibility Sequence

题目链接:ARC104F

题目大意

有一个数组 H,表示你构造的数组 X 中每一个位置的值上界,下界都是 1。
通过 X 构造数组 P,表示左边最后一个大于它的数的下标,如果没有就是 -1。
问你能构造出多少种不同的 P。

思路

发现数组长度只有 \(100\),但是值域能到 \(1e5\)
但是完全没有必要,因为你只是大小关系,你完全可以把超过数组长度的设成数组长度,顶多就是它们两两之间都不一样嘛。

然后你发现这个 \(P\) 有点像一棵树,每次 \(i\) 指向 \(P_i\),最后会形成以 \(-1\) 为根的一棵树。
那问的就是这棵树能有多少形态呗。

而且边之间你放到区间里面是不会有交叉的,因为如果有 \(x,y,z,w\) 使得 \(z\rightarrow x,w\rightarrow y\),那 \(x>z,y>w,w\geqslant z\)
\(y>w\geqslant z\)\(y>z\),那不应该 \(z\rightarrow y\) 嘛。
所以是不交叉的。

那每个子树都是一个区间,你其实可以试着区间 DP。
然后你安排值,为了给后面留位置你肯定是能放多小放多小。
\(f_{l,r,k}\) 表示一棵树 \(l,r\) 区间,里面最大值是 \(k\)
\(g_{l,r,k}\) 则表示森林的答案。

\(f_{l,r,k}=g_{l+1,r,k-1}\) 放一个根连着儿子。
\(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}\)
一种是直接放进去,一种是抬高了后面再放进去。


还有一种区间 DP 法。
前面我们从小到大考虑,我们考虑从大到小。
\(f_{l,r,k}\) 表示 \(l,r\) 区间来处理,除了自己本身的限制还有值域的全部上界 \(k\)

然后考虑从右往左枚举子树,因为子树之间那个儿子的值是要递增的。
那枚举最右边的那个子树的那个儿子的编号 \(mid\),那右边 \([mid+1,r]\) 的值域就是 \(k-1\),左边的就是 \(k\)
两边方案乘起来,所以分的情况加起来即可。

代码

sol1

#include<cstdio>
#include<iostream>
#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;
}

sol2

#include<cstdio>
#include<iostream>
#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;
}

原文地址:http://www.cnblogs.com/Sakura-TJH/p/ARC104F.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性