前言
话说好久都没写过题解了,也不过多去反思了。俗话说万事开头难,希望这是一个好的开始。
题意
有长度为 n n n 的线段, 轮流选择一个单色区间刷上 1 − n 1-n 1−n 的颜色。给出最后的颜色状态,求符合条件的方案数。
思路
考虑 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,l−1]、[l,ppl−1]、[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=i∑ppl−1r=ppl+1∑jdp[i][l−1]∗dp[l][ppl−1]∗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=i∑ppl−1dp[i][l−1]∗dp[l][ppl−1])∗(r=ppl+1∑jdp[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;
}