• 【ARC104F】Visibility Sequence(区间DP)


    Visibility Sequence

    题目链接:ARC104F

    题目大意

    有一个数组 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 zx,wy,那 x > z , y > w , w ⩾ z x>z,y>w,w\geqslant z x>z,y>w,wz
    y > w ⩾ z y>w\geqslant z y>wz y > z y>z y>z,那不应该 z → y z\rightarrow y zy 嘛。
    所以是不交叉的。

    那每个子树都是一个区间,你其实可以试着区间 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,k1 放一个根连着儿子。
    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=lr1v=1kfl,mid,vgmid+1,r,k+mid=lr1[amid+1k]v=1k1fl,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 k1,左边的就是 k k k
    两边方案乘起来,所以分的情况加起来即可。

    代码

    sol1

    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    sol2

    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
  • 相关阅读:
    【C++】KDevelop的基本使用
    什么密码,永远无法被黑客攻破?
    花 1 万块做付费咨询,值得吗?
    python爬虫概述及简单实践:获取豆瓣电影排行榜
    RabbitMQ学习笔记
    【毕业季】从高考失利到成功保研——我的大学四年
    婚礼的魅力
    HiAI Foundation助力端侧音视频AI能力,高性能低功耗释放云侧成本
    判断二叉树是否相等
    【从零开始的Java开发】1-6-5 集合综合案例:播放器管理
  • 原文地址:https://blog.csdn.net/weixin_43346722/article/details/127419919