当求前缀排列问题时,可以尝试将纯数学的逻辑转换为几何的逻辑。
例:
给定 n 个 0 和 n 个 1,它们将按照某种顺序排成长度为 2n 的序列,求它们能排列成的所有序列中,能够满足任意前缀序列中 0 的个数都不少于 1
的个数的序列有多少个。输出的答案对 109+7取模。
思路
将问题转换为从左下角走到右上角,0往右走,1往上走。就是总的路径数减去经过上红线的路径数。经过上红线的路径按上红线进行对称,一定能走到(n-1,n+1)
即计算
C
2
n
n
−
C
2
n
n
−
1
C_{2n}^n-C_{2n}^{n-1}
C2nn−C2nn−1的值
卡特兰定理:
C 2 n n − C 2 n n − 1 = ( 2 n ) ! n ! n ! − 2 n ! ( n − 1 ) ! ( n + 1 ) ! = 2 n ! n ! ( n + 1 ) ! = 1 n + 1 2 n ! n ! n ! = 1 n + 1 C 2 n n C_{2n}^n-C_{2n}^{n-1}= \frac {(2n)!} {n!n!}-\frac{2n!}{(n-1)!(n+1)!}=\frac {2n!}{n!(n+1)!}=\frac{1}{n+1} \frac {2n!}{n!n!}=\frac{1}{n+1}C_{2n}^{n} C2nn−C2nn−1=n!n!(2n)!−(n−1)!(n+1)!2n!=n!(n+1)!2n!=n+11n!n!2n!=n+11C2nn
接下来就是计算组合数的问题。
#include<iostream>
using namespace std;
typedef long long LL;
const LL mod = 1e9+7;
int n;
LL qmi(LL a,LL p,LL q){
//快速幂求逆元
LL res=1;
while(p){
if(p&1) res=res*a%q;
a=a*a%q;
p>>=1;
}
return res;
}
int main(){
cin>>n;
LL res=1;
for(int i=2*n;i>n;i--) res=res*i%mod;
for(int i=n;i>0;i--) res=res*qmi(i,mod-2,mod)%mod;
res=res*qmi(n+1,mod-2,mod)%mod;
cout<<res<<endl;
return 0;
}