题意
求 1 ∼ n 1 \sim n 1∼n,第 p o s pos pos 位为 x x x 的排列的个数,该排列需满足能通过栈将其排序成升序,对 1 0 9 + 7 10^9+7 109+7 取模。
数据范围
1 ≤ n ≤ 1 0 6 , 1 ≤ p o s , x ≤ n 1 \leq n \leq 10^6,1 \leq pos,x \leq n 1≤n≤106,1≤pos,x≤n
题解
看到题目应该先考虑满足这种条件的排列实质上是什么?
可以发现:如果一个排列能够通过栈排成升序,那么将升序的 1 ∼ n 1 \sim n 1∼n 反过来操作,也能得到这个原排列,所以该排列和该操作顺序是一一对应的。
结论即:将 1 ∼ n 1 \sim n 1∼n 通过栈能得到的所有出栈顺序的数量就是 满足能通过栈排成升序的排列数。
也就是卡特兰数。发现了这一点之后,接下来就是经典套路了——考虑卡特兰数的组合意义和几何意义
我们将入栈看作左括号 ( ( (,出栈看作右括号 ) ) )。于是如果不考虑第 p o s pos pos 位的限制,答案 C a t n Cat_n Catn 就是长度为 2 n 2n 2n 的合法括号个数,即 C 2 n n − C 2 n n − 1 C_{2n}^{n}-C_{2n}^{n-1} C2nn−C2nn−1。
接着考虑第 p o s pos pos 位的影响,第 p o s pos pos 位为 x x x 实质上就是第 p o s pos pos 个左括号要和第 x x x 个右括号配对,那么我们就枚举第 p o s pos pos 个左括号在 2 n 2n 2n 个位置中的位置 i i i,于是 i i i 前面就有 i − p o s i-pos i−pos 个右括号, p o s − 1 pos-1 pos−1 个左括号,求满足这些条件的方案数,剩下的读者们可以自己去算算组合数。
代码
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int P=1e9+7;
const int N=2e6+10;
inline int ksm(int a,int b){
int s=1;while(b){
if(b&1)s=1ll*s*a%P;
a=1ll*a*a%P;b>>=1;
}
return s;
}
int n,pos,x;
int jc[N],f[N],inv[N];
inline int qmod(int X){return (X>=P)?X-P:((X<0)?X+P:X);}
inline int C(int n,int m){
if(n<m)return 0;
return 1ll*jc[n]*inv[m]%P*inv[n-m]%P;
}
inline int cat(int n){return qmod(C(2*n,n)-C(2*n,n-1));}
inline int S(int n,int m){
if(n<0||m<0)return 0;
return qmod(C(n+m,m)-C(n+m,n+1));
}
int main(){
LL ans=0;
scanf("%d%d%d",&n,&pos,&x);
jc[0]=1;for(int i=1;i<=2*n;++i)jc[i]=1ll*jc[i-1]*i%P;
inv[0]=1;inv[n<<1]=ksm(jc[n<<1],P-2);
for(int i=2*n-1;i;--i)inv[i]=1ll*inv[i+1]*(i+1)%P;
for(int i=pos;i<=pos*2-1;++i){
int s1=S(pos-1,i-pos);
int s2=S(x-i+pos-1,x-i+pos-1);
int s3=S(n-x,n-pos-x+i-pos+1);
//cout<<"FAQ "<<s1<<" "<<s2<<" "<<s3<<" "<<i<<endl;
ans=qmod(ans+1ll*s1*s2%P*s3%P);
}
printf("%lld\n",ans);
return 0;
}