HDU 3944 DP?

来源:互联网 发布:qc访问数据库创建bug 编辑:程序博客网 时间:2024/05/23 01:23

DP?

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


Problem Description

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<k<n)
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 24 2 7
 

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

Author
phyxnj@UESTC
 

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

Recommend
xubiao

题意:从杨辉三角上选一个点,可以从左到右斜着加或者向下加,问哪种方法得到的数小一点,结果会模上一个不超过10000的素数
可以首先计算两种方法下的结果,三个数分别为m,n,p(m>=n)
1:先斜着加,再向下加,斜着会上n+1个1,再向下加会分别加上C(n+1,n),C(n+2,n),..........C(m,n),可以把1换成C(n+1,n+1),根据公式C(m,n)=C(m-1,n)+C(m-1,n-1)可以将C(n+1,n+1)+C(n+1,n)=C(n+2,n+1),再和后面的依次化简,最终可化简为C(m+1,n+1)+m
2:先向下加,再斜着加,斜着会加上m-n个1,斜着会分别加上C(m-n,0),C(m-n+1,1),..............C(m,n),C(m-n,0)可以写为C(m-n+1,0),按照上面的结论可以化简为C(m+1,n)(这是杨辉三角的一个结论,斜着的一行数相加就等于下面的一个数,这里就不证明了,方法和1的类似),所以等于C(m+1,n)+m-n
只需比较这两个数的大小即可,通过简单的几个数据不难发现,当m>=2*n时选第二种方法更小,否则就选第一种,剩下的就是用卢卡斯定理计算组合数了
要注意的是如果每次打阶乘表会TLE,=-=,自己还是太水了,题目说明素数的范围小于10000,可以将所有阶乘全部算出来用二维数组保存,剩下的就好办了

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<string>#include<stack>#include<queue>#include<deque>#include<set>#include<map>#include<cmath>#include<vector>using namespace std;typedef long long ll;typedef pair<int, int> PII;#define pii acos(-1.0)#define eps 1e-10#define pf printf#define sf scanf#define lson rt<<1,l,m#define rson rt<<1|1,m+1,r#define e tree[rt]#define _s second#define _f first#define all(x) (x).begin,(x).end#define mem(i,a) memset(i,a,sizeof i)#define for0(i,a) for(int (i)=0;(i)<(a);(i)++)#define for1(i,a) for(int (i)=1;(i)<=(a);(i)++)const int inf=0x3f3f3f3f;const int Max=10000;int pr[10003],pi[10003];//pr保存素数,pi保存当前数是第几个素数ll m,n,p,fac[2003][10003];//保存全部阶乘的数组不能开太大,不能暴力开[10001][10001]的数组,否则MLEbool vis[10003];void init(){    mem(vis,0);    pr[0]=0;    for(int i=2;i<=Max;i++)    {        if(!vis[i])pr[++pr[0]]=i,pi[i]=pr[0];//pr[0]保存当前是第几个素数,pi[i]保存的是素数i是第几个素数        for(int j=1;j<=pr[0]&&(ll)i*pr[j]<=Max;j++)        {            vis[i*pr[j]]=1;            if(!(i%pr[j]))break;        }    }    for(int i=1;i<=pr[0];i++)//打阶乘表    {        fac[i][0]=1;        for(int j=1;j<=pr[i];j++)            fac[i][j]=fac[i][j-1]*j%pr[i];    }}ll quick(ll x,ll y)//快速幂{    ll ans=1;    while(y)    {        if(y&1)ans=ans*x%p;        y>>=1;        x=x*x%p;    }    return ans;}ll Lucas(ll x,ll y)//卢卡斯定理{    ll ans=1;    while(x&&y)    {        ll a=x%p,b=y%p;        if(a<b)return 0;        ans=((ans*fac[pi[p]][a]%p)*quick(fac[pi[p]][b]*fac[pi[p]][a-b]%p,p-2))%p;//pi[p]表示输入的p是第几个素数        x/=p,y/=p;    }    return ans;}int main(){    int T=0;    init();    while(~sf("%I64d%I64d%I64d",&m,&n,&p))    {        ll a=(Lucas(m+1,n+1)+n)%p;        ll b=(Lucas(m+1,n)+m-n)%p;        pf("Case #%d: ",++T);        if(m>>1>=n)pf("%I64d\n",b);        else pf("%I64d\n",a);    }    return 0;}


原创粉丝点击