problem

luogu-P5999

solution

每个点只能跳一次,显然跳出来形成的顺序是一个排列。不难联想到最近刷的排列 dpdpdp

然后,我觉得难点在于怎么转化这个跳的规则,因为现在并不确定能否按排列 dpdpdp 一样分段做。

跳的顺序形成的排列必须满足以下两个条件之一:

  • 排列中第 iii 个元素必须小于两边的元素。
  • 排列中第 iii 个元素必须大于两边的元素。

排列的起终点(墙)为 s,ts,ts,t

转化后发现,这是可以排列 dpdpdp 的。

我们从小到大考虑 iii。设 f(i,j):f(i,j):f(i,j):iii 个数将排列分成了 jjj 段的合法方案数。

  • iii 不是墙。

    • 合并两段,jjj 段有 j−1j-1j1 个空。f(i,j)←f(i−1,j+1)∗jf(i,j)\leftarrow f(i-1,j+1)*jf(i,j)f(i1,j+1)j

    • 新插一段,可以插在任何位置,但若 sss 已过则不能插首,若 ttt 已过则不能插尾。

      f(i,j)←f(i−1,j−1)∗(j−(i>s)−(i>t))f(i,j)\leftarrow f(i-1,j-1)*\big(j-(i>s)-(i>t)\big)f(i,j)f(i1,j1)(j(i>s)(i>t))

  • iii 是墙。只能放在首尾。

    • 单独成段。f(i,j)←f(i−1,j−1)f(i,j)\leftarrow f(i-1,j-1)f(i,j)f(i1,j1)
    • 与相邻段合并,相当于延伸。f(i,j)←f(i−1,j)f(i,j)\leftarrow f(i-1,j)f(i,j)f(i1,j)

时间复杂度 O(n2)O(n^2)O(n2)

code

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mod 1000000007
#define maxn 2005
int f[maxn][maxn];
int n, s, t;signed main() {scanf( "%lld %lld %lld", &n, &s, &t );f[1][1] = 1;for( int i = 2;i <= n;i ++ )if( i ^ s and i ^ t )for( int j = 1;j <= n;j ++ )f[i][j] = (f[i - 1][j - 1] * (j - (i > s) - (i > t) ) + f[i - 1][j + 1] * j) % mod;elsefor( int j = 1;j <= n;j ++ )f[i][j] = (f[i - 1][j - 1] + f[i - 1][j]) % mod;		printf( "%lld\n", f[n][1] );return 0;
}