2018.10.04【BZOJ1856】【SCOI2010】字符串(组合数学)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/82940463

传送门


解析:

这不就是一个组合数学的乱搞题吗?

思路:

相信大家应该都做过平面坐标系上,只能向右或向上移动,问到达终点的方案数这种问题。

那么我们试着将这道题转化成上面这个问题。

令我们拥有一个空的队列,我们试着向队列中加入0或1,满足任意时刻1的个数不少于0的个数。若当前队列里面有 x x 个1和 y y 个0,则该队列当前状态对应的平面坐标为 ( x , y ) (x,y)

那么上面的问题等价于,从 ( 0 , 0 ) (0,0) 出发,不经过任何 x < y x<y 的点 ( x , y ) (x,y) 到达 ( n , m ) (n,m) 的方案数。

这是一个小学奥数题。。。

我们将坐标系旋转,可以将它看成一个帕斯卡三角形,如果不考虑不能到达的情况,那么从 ( 0 , 0 ) (0,0) 走到 ( x , y ) (x,y) 的方案数就是 ( x + y x ) \begin{pmatrix}x+y\\ x \end{pmatrix} 。再减去不合法的方案数 ( x + y y 1 ) \begin{pmatrix}x+y\\y-1\end{pmatrix} 就行了。注意特判终点在不合法范围的情况。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
 
inline
ll getint(){
    re ll num;
    re char c;
    while(!isdigit(c=gc()));num=c^48;
    while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
    return num;
}
 
inline void outint(ll a){
    static char ch[23];
    if(a==0)pc('0');
    while(a)ch[++ch[0]]=a-a/10*10,a/=10;
    while(ch[0])pc(ch[ch[0]--]^48);
}
 
cs ll mod=20100403;
 
ll fac[2000005],inv[2000005],ifac[2000005];
inline
ll C(int n,int m){
    return fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
 
int n,m;
signed main(){
    fac[0]=fac[1]=inv[0]=inv[1]=ifac[0]=ifac[1]=1;
    n=getint(),m=getint();
    for(int re i=2;i<=m+n;++i)
    fac[i]=fac[i-1]*1ll*i%mod,
    inv[i]=(mod-mod/i)*inv[mod%i]%mod,
    ifac[i]=ifac[i-1]*inv[i]%mod;
     
     
    if(n<m) outint(0);
    else outint( (C(n+m,n) - C(n+m,m-1)+mod)%mod );
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/82940463