|LIS| = 3(最长上升子序列,DP)

题意

求满足下列条件的序列个数:

  • 长度为n
  • 序列的每个元素值都在[1,m]
  • 最长严格上升子序列的长度恰好为3

数据范围

3n1000
3m10

思路

首先回顾一下最长上升子序列的做法:

  • 维护一个vector,记为L
  • 对于每个元素Ai,找到满足LjAi的最小元素的下标(二分)。如果存在的话,用Ai替换Lj。否则,将Ai添加到L的后面。
  • 最终L的长度。

因为我们只关心长度小于等于3的最长上升子序列。因此考虑fi,a,b,c,前i项的序列中长度为123的最长上升子序列中结尾的最小值为abc的数量。
转移的过程中,枚举第i个元素的数值,记为x。若1xa,那么x可以替换a,则fi,x,b,c=fi,x,b,c+fi1,a,b,c;若a+1xb,那么x可以替换b,则fi,a,x,c=fi,a,x,c+fi1,a,b,c;若b+1xc,那么x可以替换c,则fi,a,b,x=fi,a,b,x+fi1,a,b,c

这里需要注意一点,就是若a=m+1,表示长度为1的最长上升子序列不存在。b,c=m+1同理。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

const int mod = 998244353, N = 1010, M = 15;

int n, m;
ll f[N][M][M][M];

int main()
{
    scanf("%d%d", &n, &m);
    f[0][m + 1][m + 1][m + 1] = 1;
    for(int i = 1; i <= n; i ++) {
        for(int a = 1; a <= m + 1; a ++) {
            for(int b = 1; b <= m + 1; b ++) {
                for(int c = 1; c <= m + 1; c ++) {
                    for(int x = 1; x <= a && x <= m; x ++) {
                        f[i][x][b][c] = (f[i][x][b][c] + f[i - 1][a][b][c]) % mod;
                    }
                    for(int x = a + 1; x <= b && x <= m; x ++) {
                        f[i][a][x][c] = (f[i][a][x][c] + f[i - 1][a][b][c]) % mod;
                    }
                    for(int x = b + 1; x <= c && x <= m; x ++) {
                        f[i][a][b][x] = (f[i][a][b][x] + f[i - 1][a][b][c]) % mod;
                    }
                }
            }
        }
    }
    ll ans = 0;
    for(int i = 1; i <= m; i ++) {
        for(int j = 1; j <= m; j ++) {
            for(int k = 1; k <= m; k ++) {
                ans = (ans + f[n][i][j][k]) % mod;
            }
        }
    }
    printf("%lld\n", ans);
    return 0;
}
posted @   pbc的成长之路  阅读(1138)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示