[CF1178 F1] Short Colorful Strip

5 篇文章 0 订阅

前言

话说好久都没写过题解了,也不过多去反思了。俗话说万事开头难,希望这是一个好的开始。

题意

有长度为 n n n 的线段, 轮流选择一个单色区间刷上 1 − n 1-n 1n 的颜色。给出最后的颜色状态,求符合条件的方案数。

思路

考虑 d p [ i ] [ j ] \tt dp[i][j] dp[i][j] 为:在填入显现出来的最小颜色前, [ i , j ] [i,j] [i,j] 为单色,且没有连续的颜色块穿过 [ i , j ] [i,j] [i,j] 的边界,涂出 [ i , j ] [i,j] [i,j] 的结果的方案数。

我们首先找到区间最小的颜色,记其为 J z m Jzm Jzm。因为我们 d p [ i ] [ j ] \tt dp[i][j] dp[i][j] 状态设计的特点,我们当前只需单独地考虑 [ i , j ] [i,j] [i,j] 内的涂色方案。确定下 J z m Jzm Jzm 后,我们记 J z m Jzm Jzm 的位置为 p p l ppl ppl (ppl不一定是个点,也可能是一个区间,甚至是几个区间,下面的图片用一个点代替,可以自行类推)。那么这一轮涂色,我们需要涂 J z m Jzm Jzm 色,并且 p p l ppl ppl 被涂色区间包含其中。假定本次涂色区间为 [ l , r ] [l,r] [l,r] (很显然, i < = l < = r < = j i<=l<=r<=j i<=l<=r<=j)。例图如下:

在这里插入图片描述
这个时候就很明显了,我们只需要分开计算 [ i , l − 1 ] 、 [ l , p p l − 1 ] 、 [ p p l + 1 , r ] 、 [ r + 1 , j ] [i,l-1]、[l,ppl-1]、[ppl+1,r]、[r+1,j] [i,l1][l,ppl1][ppl+1,r][r+1,j] 分别的答案,乘起来就是选定一种 [ l , r ] [l,r] [l,r] 对于 [ i , j ] [i,j] [i,j] 的贡献。

写成式子为

d p [ i ] [ j ] = ∑ l = i p p l − 1 ∑ r = p p l + 1 j d p [ i ] [ l − 1 ] ∗ d p [ l ] [ p p l − 1 ] ∗ d p [ p p l + 1 ] [ r ] ∗ d p [ r + 1 ] [ j ] dp[i][j]=\sum_{l=i}^{ppl-1}\sum_{r=ppl+1}^{j}dp[i][l-1]*dp[l][ppl-1]*dp[ppl+1][r]*dp[r+1][j] dp[i][j]=l=ippl1r=ppl+1jdp[i][l1]dp[l][ppl1]dp[ppl+1][r]dp[r+1][j]

显然,上面式子的时间复杂度为 O ( n 4 ) O(n^4) O(n4)
同样显然,对于一个 p p l ppl ppl l l l r r r 的贡献可以分开计算,即

d p [ i ] [ j ] = ( ∑ l = i p p l − 1 d p [ i ] [ l − 1 ] ∗ d p [ l ] [ p p l − 1 ] ) ∗ ( ∑ r = p p l + 1 j d p [ p p l + 1 ] [ r ] ∗ d p [ r + 1 ] [ j ] ) dp[i][j]=(\sum_{l=i}^{ppl-1}dp[i][l-1]*dp[l][ppl-1])*(\sum_{r=ppl+1}^{j}dp[ppl+1][r]*dp[r+1][j]) dp[i][j]=(l=ippl1dp[i][l1]dp[l][ppl1])(r=ppl+1jdp[ppl+1][r]dp[r+1][j])

如此,就转化为一个 O ( n 3 ) O(n^3) O(n3) 的区间 d p \tt dp dp

Update

题目中说了最后答案的颜色是个排列,那么 p p l ppl ppl 一定是个点。

Code:

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define MAXN 505
#define Int register int
#define Mod 998244353
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
inline LL Abs(LL x)
{
	if (x > 0)
		return x;
	return - x;
}
inline LL Min(LL x,LL y)
{
	return x < y ? x : y;
}
inline LL Max(LL x,LL y)
{
	return x > y ? x : y;
}
inline void read(LL &x)
{
	x = 0;
	LL f = 1;
	char s = getchar();
	while (s < '0' || s > '9')
	{
		if (s == '-')
			f = -1;
		s = getchar();
	}
	while (s >= '0' && s <= '9')
	{
		x = (x << 3) + (x << 1) + (s ^ 48);
		s = getchar();
	}
	x *= f;
}
LL a[MAXN], dp[MAXN][MAXN];
LL Solve(LL i,LL j)
{
	if (i == j)
		return dp[i][j] = 1;
	if (dp[i][j] != -1)
		return dp[i][j];
	LL Jzm = INF, Ppl;
	for (Int k = i; k <= j; ++ k)
		if (a[k] < Jzm)
		{
			Jzm = a[k];
			Ppl = k;
		}
	LL AnsPre = 0;
	for (Int l = i - 1; l < Ppl; ++ l)
		AnsPre += Solve(i, l) * Solve(l + 1, Ppl - 1) % Mod, AnsPre %= Mod;
	LL AnsBack = 0;
	for (Int r = Ppl; r <= j; ++ r)
		AnsBack += Solve(Ppl + 1, r) * Solve(r + 1, j) % Mod, AnsBack %= Mod;
	return dp[i][j] = AnsPre * AnsBack % Mod;
}
int main()
{
	LL n, m;
	read( n ); read( m );
	for (Int i = 1; i <= n; ++ i)
		read( a[i] );
	memset(dp, -1, sizeof dp);
	for (Int i = 0; i <= n; ++ i)
		dp[i + 1][i] = 1;
	printf("%lld", Solve(1, n));
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值