AT_abc237_f [ABC237F] |LIS| = 3 题解
洛谷。
题意够简练了,不复述。
避坑
注意,洛谷的翻译有误,数列各项可以等于 \(M\),不是 \(M\) 以下!!
而且 “最长增加部分列” 最好改为 “最长上升子序列”。
思路
胖头鱼教练:看题吧。
我:怎么动态维护最长上升子序列长度啊……
这时,注意到我们维护最长上升子序列长度并不关心序列所有数是多少。
那么我们可以把最长上升子序列长度分别是 \(1\)、\(2\)、\(3\) 序列中的最大数维护出来,因为我们如果要更新最长上升子序列只看最大数。
那么想想算法。
这题我是从 dp 专题里看到的,所以当然是 dp 喽。
设 \(f_{i,l1,l2,l3}\) 表示当前考虑到前 \(i\) 个数,长度为 \(1\) 的最大数是 \(l1\),\(2\) 的最大数是 \(l2\),\(3\) 的最大数是 \(l3\) 的答案。
那么看看状态转移方程。
对于我们当前要插入的数字 \(v\),考虑它可以插在哪里——
\[\left\{
\begin{aligned}
\begin{flalign}
&\ f_{i,v,l2,l3}=f_{i,v,l2,l3}+f_{i-1,l1,l2,l3}\ \ (v\le l1)&
\\
&\ f_{i,l1,v,l3}=f_{i,l1,v,l3}+f_{i-1,l1,l2,l3}\ \ (l1< v\le l2)&
\\
&\ f_{i,l1,l2,v}=f_{i,l1,l2,v}+f_{i-1,l1,l2,l3}\ \ (l2< v\le l3)&
\end{flalign}
\end{aligned}
\right.
\]
最后的答案显然是 \(\sum^m_{i=1}\sum^m_{j=i+1}\sum_{k=j+1}^{m} f_{n,i,j,k}\)。
AC CODE:
#include<bits/stdc++.h>
using namespace std;
#define ljl long long
const ljl N=1001,mod=998244353;
ljl n,m,f[N][15][15][15],ans;
int main(){ios::sync_with_stdio(0);cin>>n>>m;f[0][m+1][m+1][m+1]=1;for(ljl i=1;i<=n;++i){for(ljl v=1;v<=m;++v){for(ljl l1=1;l1<=m+1;++l1){for(ljl l2=l1;l2<=m+1;++l2){for(ljl l3=l2;l3<=m+1;++l3){if(v<=l1)f[i][v][l2][l3]=(f[i][v][l2][l3]+f[i-1][l1][l2][l3])%mod;else{if(v<=l2)f[i][l1][v][l3]=(f[i][l1][v][l3]+f[i-1][l1][l2][l3])%mod;elseif(v<=l3)f[i][l1][l2][v]=(f[i][l1][l2][v]+f[i-1][l1][l2][l3])%mod;}}}}}}for(ljl i=1;i<=m;++i)for(ljl j=1;j<=m;++j)for(ljl k=1;k<=m;++k)ans=(ans+f[n][i][j][k])%mod;cout<<ans<<'\n';return 0;
}