zoj 3624

输入m,n,p,q; A(0,0),B(p,0),C(m,q),D(m,n); 路径f:由A到D;g:由B到C的路径。 沿坐标轴正方向走,f,g不能有交集。求路径对(f,g)总数mod 100000007。
因为(f,g)不相交数=(f,g)总数-(f,g)相交数;
总数:c(m+n,m)*c(m-p+q,q);
相交数:c(m+q,m)*c(m-p+n,n);

画图可知,相交即f从A进入BC 与g相交。c(m+q,m)求所有从A到C的路径数,c(m-p+n,n)求出B到D的路径数,因为AC与BD必定相交与BC区域内,且每种相交情况都
包含在内,因此两数乘积即f从A进入BC区域与g相交再到D的路径数。不过求c时因为c因为(n,m<=100000),得做相除取模,不知道该怎么求,然后搜到逆元,ab≡1(mod 100000007),可以转化为相乘,可用拓展欧几里得算法求乘法逆元。
注意:x,all-cat可能是负数,得先+mod才能取mod,不然答案还是负数。

AC代码:

#include<stdio.h>
#include<string.h>
#define N 100100
#define mod 100000007
#define ll long long
ll ex_gcd(ll a,ll b,ll &x,ll &y)
{
    if (b==0) {x=1;y=0;return a;}
    ll gcd=ex_gcd(b,a%b,x,y);
    ll t=x;
    x=y;y=t-a/b*x;
    return gcd;
}
ll ins(ll m)
{
    ll x,y;
    ex_gcd(m,mod,x,y);
    return (x%mod+mod)%mod;
}
ll c(int n,int m)//求组合数c(n,m)
{
    ll up=1,down=1;
    int i,j;
    m=m>n-m?n-m:m;
    for (i=0;i<m;++i)
    {
        up=(up*(n-i))%mod;
        down=(down*(i+1))%mod;
    }
    return (up*ins(down))%mod;
}
int main ()
{
    int m,n,p,q;
    int i,j,k;
    ll all,cat;
    while (scanf("%d%d%d%d",&m,&n,&p,&q)!=EOF)
    {
        all=(c(m+n,m)*c(m-p+q,q))%mod;
        cat=(c(m+q,m)*c(m-p+n,n))%mod;
        printf("%lld\n",((all-cat)%mod+mod)%mod);
    }
    return 0;
}

 

posted @ 2012-11-29 15:33  _hikaru__  阅读(384)  评论(0编辑  收藏  举报