给定n个0和n个1,它们将按照某种顺序排成长度为2n的序列,求它们能排列成的所有序列中,能够满足任意前缀序列中0的个数都不少于1的个数的序列有多少个。
输出的答案对1e9+7取模。
输入格式
共一行,包含整数n。
输出格式
共一行,包含一个整数,表示答案。
数据范围
1≤n≤1e5
做法:
模型抽象:将01序列看成一个在坐标轴中的走法,0代表向右走,1代表向上走,一开始在(0,0)点,因为0与1都有n个,最后一定是从(0,0)走到了(n,n)
以6为例:
在来看条件:要求任意时刻0的个数不少于1的个数,因为0是向右走,1是向上走,也就是说 在坐标轴上任意一点处,都要求 x>=y
也就是所有的点都不能超过y=x这条直线:
我们可以求出从(0,0)走到(6,6)的所有走法a,在减去所有从(0,0)到(6,6)但不符合要求的走法b,所谓不符合要求就是该走法中存在点出现在y=x以上,也就是正好在y=x+1这条线上, 最后,a-b就是答案
现在来看一下如果有点在y=x+1这条直线上的话并且终点还是(6,6),这条直线有什么特点:
找到路线第一次与y=x+1相交的点,将该点上面的路线与y=x+1做一个对称路线,发现终点跑到了(5,7):
仔细想想可以发现:
所有从(0,0)到(5,7)的路线一定可以通过类似的方式转换成从(0,0)到(6,6)且经过y=x+1这条直线的路线
从(0,0)到(6,6)的方法总数为C(12,6) , 从(0,0)到(5,7)的方法总数为C(12,5)
当n=6时,所以最后的答案为 C(12,6) - C(12,5)
……
类似的,当n=n时,答案为C(2n,n) - C(2n,n-1)
化简得出:
C(2n,n) - C(2n,n-1) = C(2n,n) / (n+1)
const int mod = 1e9+7;
int power(int a,int b,int p)
{
int res=1;
while(b) {
if(b&1) res=(ll)res*a%p; a=(ll)a*a%p; b>>=1; }
return res;
}
int C(int n,int m)
{
int res=1;
for(int i=1;i<=m;i++) res=(ll)res*(n-m+i)%mod*power(i,mod-2,mod)%mod;
return res;
}
int main()
{
int n;
cin>>n;
int ans=(ll)C(2*n,n)*power(n+1,mod-2,mod)%mod; //除n+1 转换为 乘n+1的逆元
cout<<ans<<endl;
return 0;
}