【数论】hdu3944 DP?

题目链接

题目大意:求从杨辉三角顶端往 n,k 走的路径上元素之和的最小值。 0<=k<=n<109 多组测试数据。
找找规律就知道当 nk<k 时,答案为 Cn+1k+1+k ,反之答案为 Cn+1k+nk

看起来很简单,实际上单纯地这样做是会无限TLE的……

有一个用于大整数组合数的定理:Lucas定理
Cmn % p=Lucasn,m,p
Lucasn,m,p=CabLucasn/p,m/p,p 其中 anmod p) bmmod p)
证明:以求n!%p为例,每p个为一段,而每一段求的结果一样。但需要单独处理末尾以及 p2p3p4p n/p 将p提取出来,剩下的数正是 n/p!

然而这道题还是会TLE…
再加上阶乘和逆元的预处理就完美地AK了 O(∩_∩)O~
对逆元计算有很多方法,在这里推荐一种递推的方法: ni[i]=p/ini[p % i]

在木有看题解之前,蒟蒻我也是无限TLE…
蒟蒻还需加油 ↖(^ω^)↗

附代码

#include<iostream>
#include<cstdio>
#define LL long long int
using namespace std;
const int MAXN=10000;

LL x0,y0;

int d,flag[MAXN],tmp[MAXN],prime[MAXN];//用于筛素数
int fac[1230][MAXN];//计算阶乘
int niyuan[1230][MAXN];//逆元

int a,b,p,ans,pos;

void gcd(int a,int b,int &d,LL &x0,LL &y0)
{
    if(!b)
        d=a,x0=1,y0=0;
    else
    {
        gcd(b,a%b,d,y0,x0);
        y0-=x0*(a/b);
    }
}

void init()
{
    pos=0;
    for(int i=2;i<MAXN;++i)
    {
        if(!flag[i])
        {
            prime[++pos]=i;
            tmp[i]=pos;
        }
        for(int j=1;j<=pos&&i*prime[j]<MAXN;++j)
        {
            flag[i*prime[j]]=1;
            if(i%prime[j]==0)
                break;
        }
    }

    for(int i=1;i<=pos;++i)
    {
        int k=prime[i];
        fac[i][0]=fac[i][1]=niyuan[i][0]=niyuan[i][1]=1;
        for(int j=2;j<k;++j)
        {
            fac[i][j]=(fac[i][j-1]*j)%prime[i];
            gcd(fac[i][j],k,d,x0,y0);
            niyuan[i][j]=(x0%k+k)%k;
        }
    }
}

int cal(int a,int b)
{
    if(a<b)
        return 0;
    int pos=tmp[p];
    return fac[pos][a]*niyuan[pos][b]%p*niyuan[pos][a-b];
}

int lucas(int n,int m)
{
    LL ans=1;
    while(n&&m&&ans)
    {
        ans=(ans*cal(n%p,m%p))%p;
        n/=p;
        m/=p;
    }
    return ans;
}

int main()
{
    int cnt=0;
    init();
    while(scanf("%d%d%d",&a,&b,&p)!=EOF)
    {
        if(a-b<b)
            printf("Case #%d: %d\n",++cnt,(lucas(a+1,b+1)+b)%p);
        else
            printf("Case #%d: %d\n",++cnt,(lucas(a+1,b)+a-b)%p);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值