【HDU3944 】DP?【LUCAS定理+阶乘预处理】 【杨辉三角中的点(第n行m列),问你从(0,0)点走到该点经过的点最少的权值和(只能向下走或斜着走)。同时对素数p取余】

本文探讨了在杨辉三角中寻找从顶点到指定点的最短路径问题,并给出了解决方案。利用组合数学特性简化计算过程,通过预处理阶乘及逆元提升效率。

传送门:HDU 3944

描述: 

DP?

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


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 2 4 2 7
 

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

Author
phyxnj@UESTC
 

Source
 

Recommend
xubiao   |   We have carefully selected several similar problems for you:   3940  3941  3942  3943  3945 

题意:

告诉你在一个在杨辉三角中的点(第n行m列),问你从(0,0)点走到该点经过的点最少的权值和(只能向下走或斜着走)。同时对素数p取余

思路:

根据已知的那个点(n,m),如果 n/2 >= k,那么从已知点出发,可以一直往斜的方向走,直到边界,那么 权值和就为 C(n,m)+C(n-1,m-1)……. 然后由组合数的公式两两合并可以得到 C(n+1,k)+(n-k);如果n/2< k,那么就是一直往上走,权值和就为C(n,k)+C(n-1,k)+C(n-2,k)….. +C(k+1, k)+C(k,k)+k,将C(k,k)改成C(k+1,k+1),然后与前面的组合数合并等于C(n+1,k+1)+k。因为同样根据组合数的性质当k<=n/2,k=n-k。所以只剩下第二种情况。 

同样用Lucas定理化简,但是样例有1e5组,非常大,但是p不大,不预处理会TLE。将每个数阶乘同10000以内的每个素数的取余都保存下来,到时候直接用。当一个数>=某个素数的时候,对这个素数取余肯定等于0,这相当于一个优化。

代码:

#include <bits/stdc++.h>
#define pr(x) cout << #x << "= " << x << "  " ;
#define pl(x) cout << #x << "= " << x << endl;
#define ll __int64
#define mod 1000000007
using  namespace  std;

const int N = 10005;
bool prime[N];   
int p[N],f[N][N],inv[N][N],cnt,pth[N];   
  
void isprime(){//先声明
  cnt=0;  
  memset(prime,1,sizeof(prime));  
  for(int i=2;i<N;i++){//埃式筛法
    if(prime[i]){  
      p[++cnt]=i;//记录素数
      pth[i]=cnt;//第几个素数
      for(int j=i+i;j<N;j+=i)  
        prime[j]=0;  
      }  
  }  
}  
  
int pow_mod(int a,int b,int m){  
  int ans=1;  
  a%=m;  
  while(b){  
    if(b&1){  
      ans=ans*a%m;  
      b--;  
    }  
    b>>=1;  
    a=a*a%m;  
  }  
  return ans;  
}  
  
void init(){//预处理阶乘与逆元,先声明  
  for(int i=1;i<=cnt;i++){  
    f[i][0]=inv[i][0]=1;  
    for(int j=1;j<p[i];j++){  
      f[i][j]=(f[i][j-1]*j)%p[i]; //模第i个素数的所有阶乘
      inv[i][j]=pow_mod(f[i][j],p[i]-2,p[i]); //模第i个素数的所有逆元 
    }  
  }  
}  
  
int C(int n,int m,int p){  
  if(m>n) return 0;  
  if(m==n) return 1;  
  int t=pth[p];  
  return f[t][n]*(inv[t][n-m]*inv[t][m]%p)%p;  
}  
  
int lucas(int n,int m,int p){  
  if(m==0) return 1;  
  return C(n%p,m%p,p)*lucas(n/p,m/p,p)%p;  
}  

int  main(){
  int n,k,P,kase=1;
  isprime();
  init();
  while(~scanf("%d%d%d",&n,&k,&P)){
    if(k<=n/2)k=n-k;
    printf("Case #%d: %d\n", kase++,(k%P+lucas(n+1, k+1, P))%P);
  }
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值