zoukankan      html  css  js  c++  java
  • 51Nod1362 搬箱子 排列组合,中国剩余定理

    原文链接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;
    }
    /*
    枚举斜着走了几次,然后推式子。 
    */
    

      

  • 相关阅读:
    DP入门——迷宫行走方案2
    DP入门——迷宫行走方案1
    DFS入门——八皇后问题输出方案
    matplotlib的学习3-figure图像
    matplotlib的学习2-基本用法
    matplotlib的学习1-为什么学他
    numpy的好处
    pandas的学习8-pandas-plot出图
    pandas的学习6-合并concat
    pandas的学习5-导入导出数据
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/51Nod1362.html
Copyright © 2011-2022 走看看