题目
思路
如果一个区间没有被染色,或者被全部染色
那么这个区间是很容易被计算的,也就是指我们可以考虑设计这种状态来计算答案
设$dp[i][j]$为$i$到$j$这个区间没有被染色或者全部被染色的染成正确方案的方案数
如果当前是第$i$种颜色,设其的位置为$p_i$,那么$l$~$p_i-1$和$p_i+1$到$r$的位置是可以任意染的
即我们将一个区间分成了4段
\(dp[l][r]=\sum_{k_1=l}^{p_i-1}\sum_{k_2=p_2+1}^{r}dp[l][k_1]*dp[k_1+1][p_i-1]*dp[p_i+1][k_2]*dp[k_2+1][r]\)
很明显,这个转移是$O(n^2)$的
但是我们观察这个式子,很明显可以化简成为
\(dp[l][r]=\sum_{k_1=l}^{p_i-1}dp[l][k_1]dp[k_1+1][p_i-1]*\sum_{k_2=p_i+1}^{r}dp[p_i+1][k_2]dp[k_2+1][r]\)
即转移就可以变成$O(n)$的
总共的时间复杂度为$O(n^3) $
代码
#include<iostream>
#include<cstring>
using namespace std;
const int mod=998244353;
long long dp[505][505];
//l~r未被染色或者染成同一种颜色之后染成正确颜色的种类数
int n,m;
int a[505];
long long dfs(int l,int r)
{
if(l>r)
return 1;
if(l==r)
return dp[l][r]=1;
if(dp[l][r]!=-1)
return dp[l][r];
int _ind=-1;
int minn=(1<<30);
for(int i=l;i<=r;i++)
{
if(a[i]<minn)
{
minn=a[i];
_ind=i;
}
}
long long s1=0,s2=0;
for(int len=0;_ind+len<=r;len++)
s1=(s1+dfs(_ind+1,_ind+len)*dfs(_ind+len+1,r))%mod;
for(int len=0;_ind-len>=l;len++)
s2=(s2+dfs(_ind-len,_ind-1)*dfs(l,_ind-len-1))%mod;
dp[l][r]=(max(s1,1ll)*max(s2,1ll))%mod;
return dp[l][r];
}
int main()
{
memset(dp,-1,sizeof(dp));
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
cout<<dfs(1,n);
return 0;
}