P5999 kangaroo 题解
更好的阅读体验
分析
一个妙妙的 trick。
首先原题可以转化成求有多少
首先设状态,
-
新开一段
由于后加入的一定比
i 大,所以以后插入在i 两边的数一定比i 大,所以总是合法。此时上一步操作完有j-1 段,所以有(j-1)+1=j 个空可以放。但是如果i>s 说明头不能放,同理i>t 说明尾不能放。因此有转移:dp_{i,j}=(j-[i>s]-[i>t]) \times dp_{i-1,j-1} -
接在某一段头/尾
这样的话以后一定会有一个
>i 的数接在i 另一侧,i 两侧就有一个大于它的和一个小于它的,与题意不符,所以不会有这种情况。 -
将两段连起来
此时
i 两侧的都比它小,与题意相符。上一步操作完有j+1 段,有j+1-1=j 个空可以插,因此有转移:dp_{i,j}=j\times dp_{i-1,j+1}
最后一定是整体一段,故答案为
核心代码
const int MAXN=2e3+7;
const int mod=1e9+7;
int n,s,t,dp[MAXN][MAXN];
signed main(){
qread(n,s,t);int i,j;dp[1][1]=1;
for(i=2;i<=n;i++){
for(j=1;j<=i;j++){
if(i!=s&&i!=t) dp[i][j]=(j*(dp[i-1][j+1])%mod+(j-(i>s)-(i>t))*dp[i-1][j-1]%mod)%mod;
else dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
}
}printf("%lld\n",dp[n][1]);
return 0;
}