Link
转化为排列计数,要求满足下列限制的排列({a_n})的数量:
(1.a_1=s,a_n=t)
(2.forall iin(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
e swedge i
e t:(j-c)f_{i-1,j-1}+jf_{i-1,j+1}
ightarrow f_{i,j})
(i=svee i=t:f_{i-1,j}+f_{i-1,j-1}
ightarrow 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]);
}