Luogu P5999 [CEOI2016]kangaroo
Link
转化为排列计数,要求满足下列限制的排列\(\{a_n\}\)的数量:
\(1.a_1=s,a_n=t\)
\(2.\forall i\in(1,n),(a_i-a_{i-1})(a_{i+1}-a_i)<0\)
因为出现了大小关系限制,所以考虑从小到大放数字。
设\(f_{i,j}\)表示现在放到第\(i\)个数,形成了\(j\)个连续段的方案数,同时记录\(c\)表示\(s,t\)这两个放了几个即边界放了几个。
转移的话可以把\(i\)独自放一段,也可以用\(i\)连接两个连续段,\(s,t\)特殊处理。
即:
\(i\ne s\wedge i\ne t:(j-c)f_{i-1,j-1}+jf_{i-1,j+1}\rightarrow f_{i,j}\)
\(i=s\vee i=t:f_{i-1,j}+f_{i-1,j-1}\rightarrow f_{i,j}\)
答案就是\(f_{n,1}\)。
#include<cstdio>
#include<iostream>
const int P=1000000007;
int n,s,t,c,f[2007][2007];
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
int mul(int a,int b){return 1ll*a*b%P;}
int main()
{
scanf("%d%d%d",&n,&s,&t),f[1][1]=1,c=s==1;
if(s>t) std::swap(s,t); else if(s==t) return !printf("0");
for(int i=2;i<=n;++i)
{
if(i==s||i==t) ++c;
for(int j=1;j<i;++j) i==s||i==t? (inc(f[i][j+1],f[i-1][j]),inc(f[i][j],f[i-1][j])):(inc(f[i][j+1],mul(f[i-1][j],j+1-c)),inc(f[i][j-1],mul(f[i-1][j],j-1)));
}
printf("%d",f[n][1]);
}