DP?(数论+组合数学综合题:组合数性质+预处理+组合数取摸)


Link:http://acm.hdu.edu.cn/showproblem.php?pid=3944


DP?

Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 128000/128000 K (Java/Others)
Total Submission(s): 2515    Accepted Submission(s): 782


Problem Description
DP?(数论+组合数学综合题:组合数性质+预处理+组合数取摸)_第1张图片

Figure 1 shows the Yang Hui Triangle. We number the row from top to bottom 0,1,2,…and the column from left to right 0,1,2,….If using C(n,k) represents the number of row n, column k. The Yang Hui Triangle has a regular pattern as follows.
C(n,0)=C(n,n)=1 (n ≥ 0) 
C(n,k)=C(n-1,k-1)+C(n-1,k) (0 Write a program that calculates the minimum sum of numbers passed on a route that starts at the top and ends at row n, column k. Each step can go either straight down or diagonally down to the right like figure 2.
As the answer may be very large, you only need to output the answer mod p which is a prime.
 

Input
Input to the problem will consists of series of up to 100000 data sets. For each data there is a line contains three integers n, k(0<=k<=n<10^9) p(p<10^4 and p is a prime) . Input is terminated by end-of-file.
 

Output
For every test case, you should output "Case #C: " first, where C indicates the case number and starts at 1.Then output the minimum sum mod p.
 

Sample Input
 
   
1 1 2 4 2 7
 

Sample Output
 
   
Case #1: 0 Case #2: 5
 

Author
phyxnj@UESTC
 

Source
2011 Multi-University Training Contest 11 - Host by UESTC
 

题意:给出杨辉三角数塔模型,求从顶部到第n行第k列的元素所经过的路径最小和。

编程思想:
这里用到了组合数的性质公式:C(n,k)=C(n-1,k-1)+C(n-1,k)   。由题意得到的规律:从目标点出发,每次都往左上方走,然后,走到第0列的时候就往上一直走(全为1),即最小值。故答案为 C(n,k)+C(n-1,k-1)+……+C(n-k+1,1)+C(n-k,0)+C(n-k-1,0)+……C(0,0);若按得出的公式直接Lucas算,则TLE。。必须利用组合数的性质进行预处理。把C(n-k,0)写成C(n-k+1,0)(因为都是1!),这样,上面式子那一段,可以从尾到头依次合并,最终合并为 C(n+1,k)。所以这里只要求一次组合数就可以了。 预处理技巧: 定义一个数组,所有小于10000的数的阶乘对所有小于10000的的素数进行取模,这样到lucas定理计算的时候就可以直接用了。
详见代码注释。

AC code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
#define MAXN 1000010
using namespace std;
const  int N=20;//模方程数 
const int maxx=10005;//最大素数上限 
LL a[N],mod[N];
int frc[1300][maxx];//frc[i][j]表示(j!)%(第i个素数prim[i])
int id[maxx];//素数编号 
bool vis[maxx];
int prim[maxx];//存储素数
int cnt;//素数个数
void init()//素数筛选及打表求:(j!)%(第i个素数prim[i]) 
{
	for(int i=2;i>=1;
		a=(a+a)%mod;
	}
	return ans;
}*/

LL quick_mod(LL a,LL b,LL m)//a^b%m 
{
	LL ans=1;
	a%=m;
	while(b)
	{
		if(b&1)
		{
			ans=ans*a%m;
		}			
		b>>=1;
		a=a*a%m;
	}
	return ans;
}

LL getC(LL n,LL m,int cur)//C(n,m)%mod[cur]
{
	LL p=mod[cur];//p为要取的素数模mod[cur] 
	if(m>n)
		return 0;
	if(m>n-m)
		m=n-m;
	LL ans=1;
/*	for(int i=1;i<=m;i++)
	{
		LL a=(n+i-m)%p;
		LL b=i%p;
		//ans=mul(ans,mul(a,quick_mod(b,p-2,p),p),p);//p为素数,i对p的逆元可以不用扩张欧几里得进行求解  re=i^(P-2) 
		ans = ans * (a * quick_mod(b, p-2,p) % p) % p;  
	}*///注释掉这个,然后用以下方法求解提高速率 
	//直接用打表方式求解
	int num=id[p];//素数编号 
	ans=frc[num][n]*quick_mod(frc[num][n-m]*frc[num][m]%p,p-2,p)%p;
	return ans; 
}

LL Lucas(LL n,LL k,int cur)//求C(n,k)%mod[cur] 
{
	LL p=mod[cur];
	if(k==0)
		return 1%p;
	//return getC(n%p,k%p,cur)*Lucas(n/p,k/p,cur)%p;
	return getC(n % p, k % p,cur) * Lucas(n / p, k / p,cur) % p;  
}

/*void extend_Euclid(LL a,LL b,LL &x,LL &y)
{
	if(b==0)
	{
		x=1;
		y=0;
		return;
	}
	extend_Euclid(b,a%b,x,y);
	LL tmp=x;
	x=y;
	y=tmp-a/b*y;
}

LL CRT(LL a[],LL m[],int k)//求C(n,m)%M,其中M=(m0*m2*…*m(k-1)),mi为素数,则先用a[i]存储模方程C(n,m)%mi,
{                           //m[]存储所有素数因子mi,k表示总共有k个模方程,返回C(n,m)%M的值 
	LL M=1;
	LL ans=0;
	for(int i=0;in/2)
		{
			k=n-k;
		}
		mod[0]=p;
		ans=(Lucas(n+1,k,0)+n-k)%p;
		printf("Case #%d: %d\n",T,ans);
	 } 
  	return 0;
}





你可能感兴趣的:(ACM,HDU,数论,组合数学)