2013年辽宁省赛 NEU1311 Rightmost non-zero Digit in N!

1311: Rightmost non-zero Digit in N!

时间限制: 2 Sec   内存限制: 128 MB
提交: 67   解决: 3

题目描述

The expression N!, read as "N factorial," denotes the product of the first N positive integers, where N is nonnegative. So, for example

N N!

0 1

1 1

2 2

3 6

4 24

5 120

10 3628800

For this problem, you are to write a program that can compute the rightmost non-zero digit of the factorial for N in base B.


输入要求

In the first line there is an integer T (T <= 100), indicates the number of test cases.

In each case, there is a single line with integers N and B

(0<=N<=10^7, 2<=B<=10^4).


输出要求

For each case, the output format is “Case #c: ans”. 

c is the case number start from 1.

ans is the answer of this problem. The ans is in base 10.

See the sample for more details.


假如输入

2 13 3 11 11

应当输出

Case #1: 2 Case #2: 10

提示


Case #1:


13*12*11*10*9*8*7*6*5*4*3*2*1=6227020800 base 10, which in base 3 is

121001222020102200000, so the right-most non-zero digit is 2.

 
警告:本人语文是体育老师教的。。水平有限。。可能导致你看了之后对世界失去信心。。故请慎看。。
省赛的数论题。。貌似封榜之前没有人交。。但比赛之后看有六支队过了这题。。。。(貌似我赛前偷窥的时候还看到admin没有过这题。。OTZ。。)
废话少说了。。来来说说这题的思路吧。。
有一个很直接的思路,就是将N的阶乘用唯一分解定理写开,找出决定0的个数的因子(对B进行因式分解,可以依次枚举。。方法是假设这个因子决定零的个数。。算出应该会有的零的个数。。取其中最小值。。)然后按比例除掉多余的因子。。剩下的因子用快速幂计算。。。我估计了一下,素数的个数差不多是N/lnN,故这个算法应该是一个N/lgN*lgB的算法。。。由于有大量乘法所以实际上的效率比想象中的要低。。所以交了之后果断TLE。。。
木有办法。。只好再想一个新的呗。。可是新的也不好想啊。。注意到威尔逊定理和高斯对威尔逊定理的概括。。我们可以设计一种在B是素数情况的lgN+B的算法
具体是这样的
由于B是素数,注意到k!  = (B+1)*(B+2)*(B+3)...*(B+k-1) mod B 且当k<B时我们有k! mod B 不等于 0
故在计算N!的最右非零位时可以这样算: 1*2*3...*(B-1)*(B+1)*...(B+B-1)*.......*((N/B)*B+N%B)*B*2B*...*(N/B)B再将所有的B去掉,利用上面的同余关系在O(B)时间内算出k! mod B的值lgN时间内可以将N!的最右非零位计算完。。。。
然后呢。。我就把抱着试一试的心态。。把素数的情况分开来单独做,再交了一次。。居然过了。。。orz。。。虽然程序跑的有点慢的说。。。。。。。。(貌似是一秒多来着。。。)
恩。。这就只能归结于数据水了。。可是我们不应该满足于此嘛。。。对于一般情况的高效做法肯定是存在的。。。。(为什么?感觉啊!)
嗯嗯。。。可是到底是怎么样的呢?又是一种比较直接的方法是仿造上面的做法,对于一般的B因式分解。。将所有B中的素因子拿出来,剩余部分可以仿造之前的算法。。但是拿出来的部分就不好做了。。。为什么呢?举个例子。。比如说要计算100!在12进制下的最右非零位。。我们将12分解成12 = 2 ^2 * 3这样就会有问题了。。我拿2的倍数的时候我把6拿了出来。。可是我再拿3的倍数的时候怎么办?若是不拿。。拿出的3的部分将3去掉后就不是一个连续的阶乘序列了。。要是拿了什么时候把多拿的数补回去?
所以捏。。我们需要再换个思路想想。。有这么一个结论叫做乘性函数的函数值由素数的函数值决定。。这就启示我们了对于某个进制B =  p1^a1 * p2^a2 * p3^a3... * pk*ak我们是不是可以把它看成是在
B1 = p1^a1 B2 = p2^a2 ...Bk = pk^ak进制下的解合并而成的呢?答案当然是可以啦。。。。
具体好麻烦哦。。不想敲了。。就是先算在Bi进制下的最右非零位将少乘的pi乘回来,没有去掉的其他因子乘上逆(注意到gcd(Bi, B/Bi) = 1)最后用中国剩余定理将结果合并就行啦!
其实呢这题肯定不用那么麻烦。。可是简单的办法由于我智商有限没有想到。。orz。。。要是有更好的办法欢迎交流!。。最后附上13msAC代码。。
#include <cstdio>
using namespace std;
typedef long long LL;
int T, N, B;
bool bp[10001];
int p[10001][20], num[10001];
LL fb[20], fn[20], nnum[20], cop[20], coprnz[20], mini;
void getp()
{
    for (int i=2; i<10001; i++)
    {
        if (!bp[i])for (int j=i; j<10001; j+=i)
        {
            p[j][num[j]++] = i;
            if(i!=j)bp[j] = 1;
        }
    }
}
LL mod_mult ( LL a, LL b, LL n )
{
    LL ret = 0;
    a = a % n;b = b % n;
    while ( b >= 1 )
    {
        if ( b & 1 )
        {
            ret += a;
            if ( ret >= n ) ret -= n;
        }
        a = a << 1;
        if ( a >= n ) a -= n;
        b = b >> 1;
    }
    return ret;
}
 
LL mod_exp ( LL a, LL b, LL n )
{
    LL ret = 1;
    a = a % n;
    if (b==1) return a;
    while ( b >= 1 )
    {
        if ( b & 1 )
           ret =  mod_mult(ret,a,n);
        a = mod_mult(a,a,n);
        b = b >> 1;
    }
    return ret;
}
void exgcd(LL a, LL b, LL &d, LL &x, LL &y)
{
    if (b==0)
    {
        d = a;x = 1;y = 0;
        return;
    }
    exgcd(b, a%b, d, y, x); y-=x*(a/b);
}
LL inv(LL a, LL n)
{
    LL x, d, y;
    exgcd(a, n, d, x, y);
    if (d!=1) return -1;
    return (x+n)%n;
}
void gcoprnz(int cur)
{
    int tB = cop[cur];
    int base[tB+1];base[0] = 1;
    for (int i=1; i<=tB; i++)
    {
        if (i%fb[cur])
        {
            base[i] = i * base[i-1] % tB;
        }
        else
        {
            base[i] = base[i-1];
        }
    }
    LL ans = 1;
    for (int t=N; t>0; t/=p[B][cur])
    {
        ans = ans * base[t%tB] % tB;
        ans = ans * mod_exp(base[tB], t/tB, tB)%tB;
    }
    ans = ans * mod_exp(fb[cur], nnum[cur]%fn[cur], tB) % tB;
    coprnz[cur] = ans;
    //printf ("%I64d\n", ans);
}
LL china()
{
    LL c[20];
    for (int i=0; i<num[B]; i++)
    {
        c[i] = inv(B/cop[i], cop[i]) * B/cop[i] % B;
        c[i] = c[i] * coprnz[i] % B;
    }
    LL res = 0;
    for (int i=0; i<num[B]; i++)
    {
        res += c[i];
    }
    return res%B;
}
int main()
{
    getp();
    scanf ("%d", &T);
    for (int ci=1; ci<=T; ci++)
    {
        scanf ("%d%d", &N, &B);
        int b = B;
        for (int i=0; i<num[B]; i++)
        {
            fb[i] = p[B][i];fn[i] = 0;cop[i] = 1;
            while (b%p[B][i]==0)
            {
                b /= p[B][i];
                fn[i]++;
                cop[i] *= p[B][i];
            }
            //printf ("p[B][i] = %d, time = %d\n", fb[i], fn[i]);
        }
        mini = 0;
        for (int i=0; i<num[B]; i++)
        {
            nnum[i] = 0;
            for (int j=N/p[B][i]; j>0; j/=p[B][i])
            {
                nnum[i] += j;
            }
            if (nnum[i]/fn[i]<nnum[mini]/fn[mini]) mini = i;
        }
        for (int i=0; i<num[B]; i++)
        {
            gcoprnz(i);
        }
        int u = nnum[mini]/fn[mini];
        for (int i=0; i<num[B]; i++)
        {
            //补数
            int t = (nnum[i]/fn[i]-u)*fn[i];
            coprnz[i] = coprnz[i] * mod_exp(fb[i], t, cop[i]) % cop[i];
            //乘逆
            int x = B/cop[i], v = inv(x, cop[i]);
            coprnz[i] = coprnz[i] * mod_exp(v, u, cop[i]) % cop[i];
        }
        LL res = china();
        printf ("Case #%d: ", ci);
        printf ("%d\n", (int)res);
    }
    return 0;
}
 
/**************************************************************
    Problem: 1311
    User: 20124867
    Language: C++
    Result: 正确
    Time:13 ms
    Memory:1616 kb
****************************************************************/

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
若想使用 YOLOv5 训练 NEU-DET 数据集,可以按照以下步骤进行操作: 1. 下载 NEU-DET 数据集: 首先,从数据集提供的来源下载 NEU-DET 数据集。确保你拥有训练图像和相应的标签文件。 2. 准备数据集: - 将训练图像放在一个文件夹中(如 `data/images/train/`)。 - 将与每个图像对应的标签文件放在另一个文件夹中(如 `data/labels/train/`),标签文件的格式应与 YOLOv5 要求的格式相匹配。 3. 创建数据集配置文件: 在 `data/` 目录下创建一个新的 `.yaml` 文件(如 `neu-det.yaml`),并按照以下格式填写文件内容: ```yaml train: path/to/train.txt val: path/to/val.txt nc: 6 # 类别数目 names: [crazing, inclusion, patches, pitted_surface, rolled-in_scale, scratches] # 类别名称 ``` - 将 `path/to/train.txt` 替换为包含训练图像路径的文本文件的路径。 - 将 `path/to/val.txt` 替换为包含验证图像路径的文本文件的路径。 - 将 `nc` 设置为数据集中的类别数目(在 NEU-DET 中为 6)。 - 将 `names` 设置为数据集中每个类别的名称列表。 4. 开始训练: 运行以下命令来启动训练过程: ```shell python train.py --img 640 --batch 16 --epochs 50 --data path/to/neu-det.yaml --weights yolov5s.pt ``` - `--img` 设置输入图像的大小(推荐使用 640 或 1280)。 - `--batch` 设置批量大小。 - `--epochs` 设置训练的轮数。 - `--data` 指定数据集配置文件的路径。 - `--weights` 指定预训练权重文件的路径,可以使用预训练的 YOLOv5 权重(如 `yolov5s.pt`)或者之前训练的权重文件。 5. 监控训练过程: 训练过程中会显示损失和其他指标,同时会在 `runs/train/` 目录下保存模型权重文件和训练日志。 这样,你就可以使用 YOLOv5 训练 NEU-DET 数据集了。记得替换命令中的路径参数为你自己的路径和设置适合你的训练参数。如果有其他问题,欢迎继续提问!
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值