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