[题解]P7986 [USACO21DEC] HILO P

思路

记猜测顺序的排列为 \(p_i\),首先考虑一个 \(p_i\) 不会被忽略的条件是:当 \(p_i \leq x\) 时,\(\nexists j \in [1,i),p_j \leq x \wedge p_j > p_i\);当 \(p_i > x\) 时,\(\nexists j \in [1,i),p_j > x \wedge p_j < p_i\)

考虑依次确定 \(1 \sim n\) 位置上的值,因为 \(p_i\) 是否会被忽略取决于其前缀的状态,所以设计 dp 状态时不能只记录前一个位置的取值。

因为 \(p_i \leq x\)\(p_i > x\) 的条件不同,于是我们尝试从 \(x\) 为一个分隔点入手,定义 \(dp_{i,j,0/1}\) 表示在 \([1,i + j]\) 前缀中有 \(i\) 个数 \(\leq x\)\(j\) 个数 \(> x\),上一次回答是 HI/LO 的方案数,要求构成 HILO 子串的数量就再记一个 \(f_{i,j,0/1}\) 即可。

这样定义似乎还是缺信息没法转移,这里只需要用一个插入类 dp 非常常用的一个套路。不妨钦定 \(\leq x\) 的数在 \([1,i]\) 中,\(> x\) 的数在 \([x - j + 1,n]\) 中。每次插入一个 \(\leq x\) 的数 \(t\),则将 \(t \leq p \leq i\) 的数 \(p\) 全部加一;每次插入一个 \(> x\) 的数 \(t\),则将 \(x < p \leq t\) 的数 \(p\) 全部减一。这样就可以进行转移了:

  1. \(dp_{i,j,0}\)\(f_{i,j,0}\)
    • 若上一个是 LO,则当前必须是不被忽略的 HI,那么新插入的数只能选择 \(i\) 否则将被忽略:

      \[dp_{i,j,0} \leftarrow dp_{i,j - 1,1}\\ f_{i,j,0} \leftarrow f_{i,j - 1,1} \]

    • 若上一个是 HI,当前是一个被忽略的 LO,那么新插入的数在 \([1,i)\) 范围内都可以被忽略:

    \[dp_{i,j,0} \leftarrow dp_{i - 1,j,0} \times (i - 1)\\ f_{i,j,0} \leftarrow f_{i - 1,j,0} \times (i - 1) \]

    • 若上一个是 HI,当前也是 HI,则当前 HI 是否被忽略不重要,那么新插入的数在 \([n - j + 1,n]\) 都是 HI

    \[dp_{i,j,0} \leftarrow dp_{i,j - 1,0} \times j\\ f_{i,j,0} \leftarrow f_{i,j - 1,0} \times j \]

  2. \(dp_{i,j,1}\)\(f_{i,j,1}\)
    • 若上一个是 HI,则当前必须是不被忽略的 LO,那么新插入的数只能选择 \(n - j + 1\),同时这种情况相会对 \(f\) 产生贡献:

      \[dp_{i,j,1} \leftarrow dp_{i - 1,j,0}\\ f_{i,j,1} \leftarrow f_{i - 1,j,0} + dp_{i - 1,j,0} \]

    • 若上一个是 LO,当前是一个被忽略的 HI,那么新插入的数在 \([n - j,n]\) 范围内都可以被忽略:

      \[dp_{i,j,1} \leftarrow dp_{i,j - 1,1} \times (j - 1)\\ f_{i,j,1} \leftarrow f_{i,j - 1,1} \times (j - 1) \]

    • 若上一个是 LO,当前也是 LO,则当前 LO 是否被忽略不重要,那么新插入的数在 \([1,i]\) 都是 LO

      \[dp_{i,j,1} \leftarrow dp_{i - 1,j,1} \times i\\ f_{i,j,1} \leftarrow f_{i - 1,j,1} \times i \]

答案显然为 \(f_{x,n - x,0} + f_{x,n - x,1}\)

Code

#include <bits/stdc++.h>
#define re register
#define int long long
#define Add(a,b) (((a) + (b)) % mod)
#define Mul(a,b) ((a) * (b) % mod)
#define chAdd(a,b) (a = Add(a,b))

using namespace std;

const int N = 5010;
const int mod = 1e9 + 7;
int n,x;
int r,dp[2][N][2],f[2][N][2];

inline int read(){
    int r = 0,w = 1;
    char c = getchar();
    while (c < '0' || c > '9'){
        if (c == '-') w = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9'){
        r = (r << 3) + (r << 1) + (c ^ 48);
        c = getchar();
    }
    return r * w;
}

signed main(){
    n = read(),x = read();
    for (re int i = 0;i <= x;i++,r ^= 1){
        for (re int j = 0;j <= n - x;j++) dp[r][j][0] = dp[r][j][1] = f[r][j][0] = f[r][j][1] = 0;
        if (x != n && !i) dp[0][1][0] = 1;
        if (x && i == 1) dp[1][0][1] = 1;
        for (re int j = 0;j <= n - x;j++){
            if (i){
                chAdd(dp[r][j][0],Mul(dp[r ^ 1][j][0],i - 1));
                chAdd(f[r][j][0],Mul(f[r ^ 1][j][0],i - 1));
                chAdd(dp[r][j][1],dp[r ^ 1][j][0]);
                chAdd(f[r][j][1],Add(f[r ^ 1][j][0],dp[r ^ 1][j][0]));
                chAdd(dp[r][j][1],Mul(dp[r ^ 1][j][1],i));
                chAdd(f[r][j][1],Mul(f[r ^ 1][j][1],i));
            }
            if (j){
                chAdd(dp[r][j][0],dp[r][j - 1][1]);
                chAdd(f[r][j][0],f[r][j - 1][1]);
                chAdd(dp[r][j][0],Mul(dp[r][j - 1][0],j));
                chAdd(f[r][j][0],Mul(f[r][j - 1][0],j));
                chAdd(dp[r][j][1],Mul(dp[r][j - 1][1],j - 1));
                chAdd(f[r][j][1],Mul(f[r][j - 1][1],j - 1));
            }
        }
    } printf("%lld",Add(f[x & 1][n - x][0],f[x & 1][n - x][1]));
    return 0;
}
posted @ 2026-01-15 21:32  WBIKPS  阅读(2)  评论(0)    收藏  举报