CF1178F1 Short Colorful Strip 题解

· · 题解

Short Colorful Strip

考虑设 f[i,j] 表示:假设区间 [i,j] 里面一开始所有格子的颜色都是相同的,那么,染成目标状态共有多少种染法。

我们找到 [i,j] 中最小的那个颜色,设为 mp。则显然,我们下一步要染上 mp 这种颜色。

设最终在位置 p_{mp} 上染上了颜色 mp。则我们可以在所有这样的区间 [k,l] 上染上 mp(i \le k \le p_{mp} \le l \le j)

或许你会以为这意味着 f[i,j]=\sum\limits_{k=i}^{p_{mp}}\sum\limits_{l=p_{mp}}^jf[i,k-1] \times f[k,l] \times f[l+1,j]

但是,这样是错误的,因为当 [k,l]=[i,j] 时,f[i,j] 便无法从子状态转移过来!

我们考虑拆开 f[k,l]。因为再往后的染色中,位置 p_{mp} 一定没有再被染色过,因此有 f[k,l]=f[k,p_{mp}-1] \times f[p_{mp}+1,l]

f[i,j]=\sum\limits_{k=i}^{p_{mp}} \sum\limits_{l=p_{mp}}^{j} f[i,k-1] \times f[k,p_{mp}-1] \times f[p_{mp}+1,l] \times f[l+1,j]

特殊定义一下,对于 f[i,j],如果 i>j,则 f[i,j]=1。这也是为了转移的正确(在应用上述式子时可能会调用到这样的 f[i,j]

上面的转移是 O(n^4) 的;但当我们拆开两个 \sum,就可以把它化成 O(n^3) 的。

f[i,j]=(\sum\limits_{k=i}^{p_{mp}}f[i,k-1] \times f[k,p_{mp}-1]) \times (\sum\limits_{l=p_{mp}}^jf[p_{mp}+1,l] \times f[l+1,j])

前后两个括号内的内容互不干涉,故可以分开计算。

复杂度 O(n^3)

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
const int N=510;
int n,m;
int num[N],f[N][N];
inline int read(){
    char c=getchar();int f=1,x=0;
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++) num[i]=read();
    for(int i=1;i<=n+1;i++)for(int j=0;j<i;j++) f[i][j]=1;
    for(int i=1;i<=n;i++) f[i][i]=1;
    for(int l=2;l<=n;l++){
        for(int i=1,j=i+l-1;j<=n;i++,j++){
            int x=i,A=0,B=0;
            for(int k=i;k<=j;k++) if(num[k]<=num[x]) x=k;
            for(int k=x;k>=i;k--) (A+=(1LL*f[i][k-1]*f[k][x-1]%mod))%=mod;
            for(int k=x;k<=j;k++) (B+=(1LL*f[x+1][k]*f[k+1][j]%mod))%=mod;
            f[i][j]=1LL*A*B%mod;
        }
    }
    printf("%d\n",f[1][n]);
    return 0;
}