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; }