题面
首先打广告: 由于这题需要用到逆元,所以!!!
如果不会逆元的请看这里
−
−
−
−
−
>
----->
−−−−−> 详解数论从入门到入土
想更好地把逆元应用到欧拉函数、欧拉定理、费马小定理、中国剩余定理请看这里
−
−
−
−
−
>
----->
−−−−−>数论1
想更好地把逆元应用到组合数、扩展欧几里得请看这里
−
−
−
−
−
>
----->
−−−−−>数论2
okk 上题解
这道题应用到了小学奥数 概率论中的一个比较骚的操作
可以考虑把
1
1
1的个数与
0
0
0的个数的和看成
x
x
x坐标,
1
1
1的个数与
0
0
0的个数的差看成
y
y
y坐标,那么如下图:
向右上走(
x
x
x坐标加
1
1
1,
y
y
y坐标加
1
1
1)就表示这个字符选择
1
1
1。
向右下走(
x
x
x坐标加
1
1
1,
y
y
y坐标减
1
1
1)就表示这个字符选择
0
0
0。
那么显然从
(
0
,
0
)
(0,0)
(0,0)走
n
+
m
n+m
n+m步正好到
(
n
+
m
,
n
−
m
)
(n+m,n-m)
(n+m,n−m)的方案数就是
C
(
n
+
m
,
m
)
C(n+m,m)
C(n+m,m)。
可以理解为从
n
+
m
n+m
n+m步中抽出
m
m
m步向右下走
考虑限制条件:任意前缀中
1
1
1的个数不少于
0
0
0的个数,也就是这条路径不能经过直线
y
=
−
1
y=−1
y=−1 那么我们又要用一个东西叫做反射原理:
从点
A
A
A到点
B
B
B的路径中 经过这个点下方直线
l
l
l的方案数,就等于点
A
A
A关于直线
l
l
l对称的点
A
′
A'
A′到点
B
B
B的方案数。
很好理解对吧 画画图就可以看出来了
所以从
(
0
,
0
)
(0,0)
(0,0)到
(
n
+
m
,
n
−
m
)
(n+m,n-m)
(n+m,n−m)经过
y
=
−
1
y=-1
y=−1的方案数 就是
(
0
,
−
2
)
(0,-2)
(0,−2) 到点
(
n
+
m
,
n
−
m
)
(n+m,n-m)
(n+m,n−m)的方案数
即从
(
0
,
−
2
)
(0,-2)
(0,−2)向右上走
n
+
1
n+1
n+1步 向右下走
m
−
1
m-1
m−1步的方案数 就等于
C
(
n
+
m
,
m
−
1
)
C(n+m,m-1)
C(n+m,m−1)
那么符合题意的方案数就是总方案数减去不符合题意的方案数:
C
(
n
+
m
,
m
)
−
C
(
n
+
m
,
m
−
1
)
C(n+m,m)-C(n+m,m-1)
C(n+m,m)−C(n+m,m−1)
直接
O
(
n
)
O(n)
O(n)预处理逆元就行
如果不会逆元的请看这里
−
−
−
−
−
>
----->
−−−−−> 详解数论从入门到入土
想更好地把逆元应用到欧拉函数、欧拉定理、费马小定理、中国剩余定理请看这里
−
−
−
−
−
>
----->
−−−−−>数论1
想更好地把逆元应用到组合数、扩展欧几里得请看这里
−
−
−
−
−
>
----->
−−−−−>数论2
#include<bits/stdc++.h>
using namespace std;
#define N int(1e6+100)
typedef long long ll;
const ll mod=20100403;
ll n,m,inv[N<<1],fac[N<<1],facinv[N<<1];
ll C(ll n, ll m){return fac[n]*facinv[m]%mod*facinv[n-m]%mod;}
int main(){
scanf("%lld%lld",&n,&m);
inv[1]=fac[0]=fac[1]=facinv[1]=facinv[0]=1;
for(int i=2;i<n+m+100;i++)
{
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
facinv[i]=facinv[i-1]*inv[i]%mod;
fac[i]=fac[i-1]*i%mod;
}
printf("%lld\n",(C(n+m,m)-C(n+m,m-1)+mod)%mod);
}