P5999 [CEOI2016] kangaroo的TJ

[CEOI2016] kangaroo

其实就是给你一个p1,pn确定,其余未知的排列,求有多少个合法的排列,满足一个数要么比他相邻的两边都大,要么比他相邻的两边都小。

我们若是依次考虑每p1,2,,n,由于他的取值还和后面有关,我们不好考虑,考虑依次将i=1,2,,n填入当前的序列。

dpi,j表示现在填到了第i个数,填出来了j个块的方案数。(每一块相当于是在排列上连续的一段,但需要注意的是,我们并不关心这一段的具体位置)。

先考虑i=s/t的情况:

很明显,他们要么是会加入最左边/最右边的块,要么是单独在最左边/最右边作为一个块,所以:

fi,j=fi1,j+fi1,j1

其他情况下,我们分情况考虑:

  1. 若他把两个块合并起来,由于之前块的每一个数都比他小,所以明显可以,则fi,jfi1,j+1×j(之前的j+1个块中有j个空隙,可以填入任意一个)。
  2. 他加入一个块,那么此时他旁边的那个肯定小于它,但未来加入的肯定大于他,不合法。
  3. 若他单独开一个块,很明显接下来插入他两边的都比他大,则有j1+1=j个位置,但是需要注意的是,若他比s大,则不能放到最左边(同s靠一起),比t大同理,则fi,jfi1,j1×(j[i>s][i>t])

注意到这样子我们构造出来的每一段都是如:小->大->小->大->小 这样的形式的,所以保证了dp式子的正确性(主要是第一个情况)。

初始状态为f1,1=1,最终答案为fn,1

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;
inline int rd(){
int x=0,f=1; char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if (ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
return x*f;
}
const int N=2e3+5,INF=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
int n,s,t;
int f[N][N];
signed main(){
n=rd(),s=rd(),t=rd();
f[1][1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
if(i==s||i==t) f[i][j]=(f[i-1][j]+f[i-1][j-1])%mod;
else{
f[i][j]+=f[i-1][j+1]*j%mod;
f[i][j]+=f[i-1][j-1]*(j-(i>s)-(i>t))%mod;
f[i][j]%=mod;
}
}
}
printf("%lld\n",f[n][1]);
return 0;
}
posted @   123456xwd  阅读(18)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示