CF1178F1 Short Colorful Strip 题解

xuantianhao

2023-10-10 13:13:21

Solution

# [Short Colorful Strip](https://www.luogu.com.cn/problem/CF1178F1) 考虑设 $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)$。 ```cpp #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; } ```