[题解]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\) 全部减一。这样就可以进行转移了:
- \(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 \] - 若上一个是
- \(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;
}

浙公网安备 33010602011771号