HDU3944

今天做到的一个题目,题目的解法依据Lucas定理。题目描述:

 

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

 

 题目的意思是说,在杨辉三角上找一条路径,每次必须往下走一行,使得这个路径上所有数字的总和最小,输出取模值。

 

这里对于组合数有一个变换。C(n-1,k-1)+C(n-1,k)=C(n,k)

同时,仔细观察杨辉三角就知道,对于每行,从左至中间都是递增的,同时又根据对称性,把所有大于每行中点的点对称到左边来。

这样从目标点出发,每次都往左上方走,然后,走到第0列的时候就往上一直走(全为1),即最小值。

这样其结果可以表示为:

C(n,k)+C(n-1,k-1)+……+C(n-k+1,1)+C(n-k,0)+C(n-k-1,0)+……C(0,0)

注意观察这个式子和我开始列出来的那个公式,如果我们把C(n-k,0)写成C(n-k+1,0)(为什么?因为都是1呗!),

这样,上面式子昨天那一段,可以从尾到头依次合并,最终合并为C(n+1,k)ORZ。所以这里只要求一次组合数就可以了呢。

但是问题又来了,题目明确说了有100000组数据,而一般用Lucas定理求组合数的时间复杂度和P同级。这里P<=10000。所以这样做就会超时呢。

怎么办才好呢?这里有点暴力的感觉。不对,应该说是一个比较强力的预处理。

定义一个数组,所有小于10000的数的阶乘对所有小于10000的的素数进行取模,这样到lucas定理计算的时候就可以直接用了哦,ORZ

这里有一个致命的细节:如果在预处理的时候直接取余,那么显然对于所有大于p(某一个质数)的阶乘的余数都为0了哦。

所以我们在求余的时候要把所有因子去掉。

在求解lucas的时候对A中所含的p因子的个数和(A-B)和B中所含因子p的个数的和进行比较,如果大于显然就可以直接返回0了(想想问什么?显然……),同时又不可能是小于的情况(想想为什么?),所以只有相等的时候我们才要继续算下去,同时相等的话又不需要考虑p因子了,ORZ)。

说到这咯,这个题目的所有的坑基本上都已经跳过去了。

下面上我的代码:

#include <iostream>

#include <cstdio>

#include <cstring>

#define maxn 10000

using namespace std;

 

int a[1300][maxn],pri[1300],tot=0,pos[maxn];

int n,k,p;

bool b[maxn];

 

void getpri()

{

    for (int i=2; i<maxn; i++)

    {

        if (!b[i]) pri[++tot]=i,pos[i]=tot;

        for (int j=i+i; j<maxn; j+=i) b[j]=true;

    }

}

 

void init()

{

    int tep;

    for (int i=1; i<=tot; i++)

    {

        tep=a[i][0]=1;

        for (int j=1; j<maxn; j++)

        {

            tep=(tep*j);

            while (tep%pri[i]==0) tep/=pri[i];

            tep%=pri[i];

            a[i][j]=tep;

        }

    }

}

 

int exp_mod(int A,int B,int p)

{

    int ans=1;

    while (B)

    {

        if (B&1) ans=(ans*A)%p;

        B>>=1;

        A=(A*A)%p;

    }

    return ans;

}

 

int cm(int A,int B,int p)

{

    int tep=exp_mod(a[pos[p]][B],p-2,p)*exp_mod(a[pos[p]][A-B],p-2,p);

    tep%=p;

    return (a[pos[p]][A]*tep)%p;

}

 

int count(int A,int p)

{

    if (A==0) return 0;

    return A/p+count(A/p,p);

}

 

int lucas(int A,int B,int p)

{

    if (B==0) return 1;

    if (count(A,p)>count(B,p)+count(A-B,p)) return 0;

    return lucas(A/p,B/p,p)*cm(A%p,B%p,p);

}

 

int main()

{

    int cas=0;

    getpri();

    init();

    while (scanf("%d%d%d",&n,&k,&p)!=EOF)

    {

        if (k>n-k) k=n-k;

        printf("Case #%d: %d\n",++cas,(lucas(n+1,k,p)+n-k)%p);

    }

    return 0;

}

 

 

 

转载于:https://www.cnblogs.com/lochan/p/3306920.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值