题面
首先打广告: 由于这题需要用到逆元,所以!!!
如果不会逆元的请看这里 − − − − − > -----> −−−−−> 详解数论从入门到入土
想更好地把逆元应用到欧拉函数、欧拉定理、费马小定理、中国剩余定理请看这里 − − − − − > -----> −−−−−>数论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
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);
}