原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1362.html
题目传送门 - 51Nod1362
题意
题解
首先考虑枚举斜着走了几次。假设走了 $k$ 次,那么显然竖着走了 $n-k$ 次,将他们排列一下,有 $inom{n}{k}$ 种排列。
设往下走 $k$ 次,往右走最多 $m$ 次的方案数为:
$$F_{n,m}=sum_{i=0}^m inom{i+n}{n}$$
则
$$egin{eqnarray*}F_{n,m}&=&sum_{i=0}^m inom{i+n}{n}\&=&sum_{i=0}^{m} left(inom{i+n-1}{n}+inom{i+n-1}{n-1} ight)\&=&sum_{i=1}^{m}inom{(i-1)+n}{n}+sum_{i=0}^{m} inom{i+(n-1)}{(n-1)}\&=&F_{n,m-1}+F_{n-1,m}end{eqnarray*}$$
考虑计算边界情况的 $F$ 值,有:
$$egin{cases}F_{i,0}=inom{i}{i}=1\F_{0,i}=sum_{j=0}^{i}inom{j}{j}=i+1end{cases}$$
不难发现,
$$F_{n,m}=inom{n+1}{m}$$
所以每一个 $F_{n,m}$ 都可以 $O(n)$ 来求,但是由于模数并不是大素数,所以我们需要分解模数并用互质情况下的 CRT 合并,所以要带一个 $log$ 。
于是,最终答案为
$$sum_{i=0}^{n}inom{n}{i}inom{n+m-i+1}{n+1}$$
总时间复杂度为 $O(n^2log m)$ 。
由于我偷了个懒,没有预处理,所以我的代码的时间复杂度为 $O(n^2log^2 m)$ 。
代码
#include <bits/stdc++.h> using namespace std; const int N=805; int n,m,X; int p[20],q[20],cnt=0; int C[N][N]; void Get_Small_C(int mod){ memset(C,0,sizeof C); for (int i=0;i<=n;i++) C[i][i]=C[i][0]=1; for (int i=1;i<=n;i++) for (int j=1;j<i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; } void Get_Factors(int x){ cnt=0; for (int i=2;i*i<=x;i++) if (x%i==0){ p[++cnt]=i,q[cnt]=0; while (x%i==0) x/=i,q[cnt]++; } if (x>1) p[++cnt]=x,q[cnt]=1; } int Pow(int x,int y,int mod){ int ans=1; for (;y;y>>=1,x=1LL*x*x%mod) if (y&1) ans=1LL*ans*x%mod; return ans; } int Phi(int p,int q){ return (p-1)*Pow(p,q-1,X+1); } int Large_C(int n,int m,int p,int q){ if (m>n||m<0) return 0; int pw=Pow(p,q,X+1),phi=Phi(p,q); int cntp=0,C=1; for (int i=1;i<=m;i++){ int a=n-i+1,b=i; while (a%p==0) a/=p,cntp++; while (b%p==0) b/=p,cntp--; C=1LL*C*a%pw*Pow(b,phi-1,pw)%pw; } return 1LL*C*Pow(p,cntp,pw)%pw; } void ex_gcd(int a,int b,int &x,int &y){ if (!b){ x=1,y=0; return; } ex_gcd(b,a%b,y,x); y-=x*(a/b); } int CRT(int *v,int n){ int A=0,M=1; for (int i=1;i<=n;i++){ int a=v[i],m=Pow(p[i],q[i],X+1); int t=a-A,x,y; ex_gcd(M,m,x,y); x=1LL*x*t%m; A=(1LL*x*M+A)%(M*m); M*=m; } return (A+X)%X; } int Large_C(int n,int m){ if (m>n||m<0) return 0; int res[20]; for (int i=1;i<=cnt;i++) res[i]=Large_C(n,m,p[i],q[i]); return CRT(res,cnt); } int main(){ while (~scanf("%d%d%d",&n,&m,&X)){ Get_Small_C(X); Get_Factors(X); int ans=0; for (int i=0;i<=n;i++) ans=(1LL*C[n][i]*Large_C(n+m-i+1,n+1)+ans)%X; printf("%d ",ans); } return 0; } /* 枚举斜着走了几次,然后推式子。 */