DP?(HDU-3944)


1 题意

  给定一个杨辉三角:
在这里插入图片描述
  再给定杨辉三角中的一个点 ( n , m ) (n,m) (n,m),要求从 ( 0 , 0 ) (0,0) (0,0)出发,按照 f i g u r e 2 figure 2 figure2所示两个方向行走,走到 ( n , m ) (n,m) (n,m)所需要的最小花费(花费等于经过数字之和,不包括 ( 0 , 0 ) (0,0) (0,0))。
  链接:link


2 思路

  有一个很明显的贪心思想:在杨辉三角中,两侧的数字总是比中间的小,所以行走的时候尽量靠边行走即可。
  根据上述贪心思想可以得出:当 m ⩽ ⌈ n 2 ⌉ m \leqslant \lceil \frac{n}{2} \rceil m2n时,从 ( 0 , 0 ) (0,0) (0,0) ( n , m ) (n,m) (n,m)的最小花费为 ( n m ) + ( n − 1 m − 1 ) + . . . + ( n − m 0 ) + n − m \binom{n}{m}+\binom{n-1}{m-1}+...+\binom{n-m}{0}+n-m (mn)+(m1n1)+...+(0nm)+nm
  利用 P a s c a l Pascal Pascal公式 ( n − 1 m ) + ( n − 1 m − 1 ) = ( n m ) \binom{n-1}{m}+\binom{n-1}{m-1}=\binom{n}{m} (mn1)+(m1n1)=(mn)对上式化简:
  原式 = ( n m ) + ( n − 1 m − 1 ) + . . . + ( n − m + 1 0 ) + n − m = ( n + 1 m ) + n − m =\binom{n}{m}+\binom{n-1}{m-1}+...+\binom{n-m+1}{0}+n-m=\binom{n+1}{m}+n-m =(mn)+(m1n1)+...+(0nm+1)+nm=(mn+1)+nm
  当 m > ⌈ n 2 ⌉ m > \lceil \frac{n}{2} \rceil m>2n时,由对称性,转而求 n − m n-m nm即可。
  最后利用 L u c a s Lucas Lucas公式求解: ( s p + q t p + r ) ≡ ( s t ) ( q r ) (   m o d   ( p ) ) \left(\begin{array}{l} s p+q \\ t p+r \end{array}\right) \equiv\left(\begin{array}{l} s \\ t \end{array}\right)\left(\begin{array}{l} q \\ r \end{array}\right)(\bmod (p)) (sp+qtp+r)(st)(qr)(mod(p))
  在利用 L u c a s Lucas Lucas公式求解组合数时,会涉及到计算 ( q r ) ( 0 ⩽ r ⩽ q < p ) \binom{q}{r}(0 \leqslant r \leqslant q < p) (rq)(0rq<p),可以事先把 x ! ( 0 ⩽ x < p ) x!(0 \leqslant x < p) x!(0x<p)在模 p p p情况下的值及其逆元求出来(由于 ( 0 ⩽ x < p ) (0 \leqslant x < p) (0x<p),所以 x ! x! x!的逆元一定存在),之后便能在 O ( 1 ) \mathcal{O}(1) O(1)的时间求出 ( q r ) \binom{q}{r} (rq)

2.1 时间复杂度分析

  预处理中,对每个素数 p p p,都要计算 x ! ( 0 ⩽ x < p ) x!(0 \leqslant x < p) x!(0x<p)的模和逆元,所以时间复杂度为 m a t h c a l O ( n + p log ⁡ ( p ) ϕ ( n ) ) mathcal{O}(n+p\log(p)\phi(n)) mathcalO(n+plog(p)ϕ(n)),而 L u c a s Lucas Lucas计算的时间复杂度为 O ( log ⁡ ( n ) ) \mathcal{O}(\log(n)) O(log(n)),假设要询问 t t t次,那么总的时间复杂度为 O ( n + p log ⁡ ( p ) ϕ ( n ) + t log ⁡ ( n ) ) \mathcal{O}(n+p\log(p)\phi(n)+t\log(n)) O(n+plog(p)ϕ(n)+tlog(n))

2.2 实现

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e4+10;
const int M=1300;
int pos[N]={0},rvs[M][N]={0},ftr[M][N]={0},prm[N],vis[N],cnt=0;
int qpow(int a,int b,int p){
    int res=1,rem=a;
    while(b){
        if(b&1) res=res*rem%p;
        rem=rem*rem%p;
        b>>=1;
    }
    return res;
}
void init(){
    for(int i=2;i<N;i++){
        if(!vis[i]){
            prm[cnt++]=i;
            pos[i]=cnt;
            ftr[cnt][0]=rvs[cnt][0]=1;
            for(int j=1,k=1;j<i;j++){
                k=k*j%i;
                ftr[cnt][j]=k;
                rvs[cnt][j]=qpow(k,i-2,i);
            }
        }
        for(int j=0;j<cnt&&i*prm[j]<N;j++){
            vis[i*prm[j]]=1;
            if(i%prm[j]==0) break;
        }
    }
}
int combination(int n,int m,int p){
    if(m>n) return 0;
    int idx=pos[p];
    return ftr[idx][n]*rvs[idx][m]%p*rvs[idx][n-m]%p;
}
int Lucas(int n,int m,int p){
    if(m==0) return 1;
    return combination(n%p,m%p,p)*Lucas(n/p,m/p,p)%p;
}
int main(){
    int k=1,n,m,p;
    init();
    while(~scanf("%d %d %d",&n,&m,&p)){
        if(2*m>n) m=n-m;
        printf("Case #%d: %d\n",k++,((Lucas(n+1,m,p)+n-m)%p+p)%p);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值