【ARC104F】Visibility Sequence(区间DP)

Visibility Sequence

题目链接:ARC104F

题目大意

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

思路

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

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

而且边之间你放到区间里面是不会有交叉的,因为如果有 x,y,z,w 使得 zx,wy,那 x>z,y>w,wz
y>wzy>z,那不应该 zy 嘛。
所以是不交叉的。

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

fl,r,k=gl+1,r,k1 放一个根连着儿子。
gl,r,k=mid=lr1v=1kfl,mid,vgmid+1,r,k+mid=lr1[amid+1k]v=1k1fl,mid,kgmid+1,r,v
一种是直接放进去,一种是抬高了后面再放进去。


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

然后考虑从右往左枚举子树,因为子树之间那个儿子的值是要递增的。
那枚举最右边的那个子树的那个儿子的编号 mid,那右边 [mid+1,r] 的值域就是 k1,左边的就是 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; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/ARC104F.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示