zoukankan      html  css  js  c++  java
  • hdu 3944 DP? (Lucas定理+预处理)

    题意:给你一个杨辉三角,每一行和每一列都是从0开始,给两个数n,k,代表杨辉三角的第n行第k列,和一个素数p,让你计算从(0,0)走到(n,k)的最小花费sum%p;

    思路:让花费尽量小,那就让走过的1尽量多,我们可以有两种走法

    第一种:先向下走再斜着走

    第二种:想斜着走再向下走

    第一种分析:我们走k+1个1,然后走C(k+1,k)+C(k+2,k)+...+C(n,k),我在这个式子中加入C(k+1,k+1),根据C(n,m)=C(n-1,m)+C(n-1,m-1),我们可以将这个式子合并,最后等于C(n+1,k+1),由于我们之前加入了C(k+1,k+1),这样第一种方法的最终结果是k+C(n+1,k+1)

    第二种分析:我们先走了n-k个1,然后走C(k,0)+C(k+1,1)+C(k+2,2)+...+C(n,k),用C(k+1,0)把C(k,0)替换掉,将式子合并,最后等于C(n+1,k),最终结果是n-k+C(n+1,k)

    这两种方法哪种走过的1少就用哪种,只需比较n与n-k的大小就行

    当n<n-k就用第一种,反之用第二种

    由于n,k的范围比较大,而p又是素数,所以用Lucas定理求组合数

    代码:

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <algorithm>
    #include <cstring>
    #define ll long long
    
    using namespace std;
    const int maxn=10005;
    bool isprime[maxn];
    int prime[maxn],prime_th[maxn];
    int cnt;
    int  inv_by_prime_th[maxn][maxn];
    int  f_use_to_inv[maxn][maxn];
    
    void get_prime_and_primeth()
    {
        cnt=0;
        memset(isprime,true,sizeof(isprime));
        for(int i=2;i<maxn;i++)
        {
            if(isprime[i])
            {
                prime[++cnt]=i;
                prime_th[i]=cnt;
                for(int j=i+i;j<maxn;j+=i)
                {
                    isprime[j]=false;
                }
            }
        }
    }
    
    int qmod(int a,int b,int mod)
    {
        int ans=1;
        a=a%mod;
        while(b)
        {
            if(b&1)
            {
                ans=(ans*a)%mod;
                b--;
            }
            b=b/2;
            a=(a*a)%mod;
        }
        return ans;
    }
    
    void init()
    {
        for(int i=1;i<=cnt;i++)
        {
            inv_by_prime_th[i][0]=f_use_to_inv[i][0]=1;
            for(int j=1;j<prime[i];j++)
            {
                f_use_to_inv[i][j]=(f_use_to_inv[i][j-1]*j)%prime[i];
                inv_by_prime_th[i][j]=qmod(f_use_to_inv[i][j],prime[i]-2,prime[i]);
            }
        }
    }
    
    int com(int n,int m,int p)
    {
        if(n<m) return 0;
        else if(n==m) return 1;
        else
        {
            int primeth=prime_th[p];
            return (f_use_to_inv[primeth][n]*((inv_by_prime_th[primeth][m]*inv_by_prime_th[primeth][n-m])%p))%p;
        }
    }
    
    int Lucas(int n,int m,int p)
    {
        if(m==0) return 1;
        else return (com(n%p,m%p,p)*Lucas(n/p,m/p,p))%p;
    }
    
    int main(int argc, char const *argv[])
    {
        int cas=1;
        get_prime_and_primeth();
        init();
        //ios::sync_with_stdio(false);
        int n,m,p;
        while(scanf("%lld %lld %lld",&n,&m,&p)!=-1)
        {
            printf("Case #%d: ",cas++);
            if(m<=n/2)  m=n-m;
            printf("%lld
    ",(Lucas(n+1,m+1,p)+m%p)%p);
        }
        return 0;
    }
  • 相关阅读:
    代码抄袭检测系统的设计和实现(1)--需求分析和基础架构
    Protobuf C/C++实战笔记(1)
    自动构建Makefile(1)--C/C++编译流程&Makefile规则简介
    初识storm
    Ubuntu 16.04LTS安装Nginx
    redis内存监控与回收
    RabbitMQ交换机规则实例
    git合并分支上指定的commit
    git忽略掉文件权限检查
    git修改文件权限方式
  • 原文地址:https://www.cnblogs.com/simplekinght/p/6986268.html
Copyright © 2011-2022 走看看