题目描述

很简单,给出正整数$n$,求出$n!$在十进制表示下的从最末非零位开始的总共$k$位。


输入格式

第一行一个正整数$T$,表示有$T$组数据
接下来$T$行,每行两个正整数$n$和$k$。


输出格式

每组测试数据,按照高位到低位的顺序输出,共$k$位,包含前导$0$(若高位不足,用前导$0$补足)。


样例

样例输入:

3
1 1
5 1
10 2

样例输出:

1
2
88


数据范围与提示

对于$20\%$的数据,$n\leqslant 100,k=1,2,3$分别占$10\%,5\%,5\%$
对于另外$30\%$的数据,$n\leqslant 10^7,k=1,2,3$分别占$10\%,10\%,10\%$
对于另外$30\%$的数据,$n\leqslant 10^{18},k=1,2,3$分别占$10\%,10\%,10\%$
对于另外$20\%$的数据,$n\leqslant 10^{100},k=1,2,3$分别占$5\%,5\%,10\%$
$T\leqslant 100$


题解

一道$200$多行的数学题……

讲一下推到过程(其实主要是学长的……)

为方便,不妨做如下定义:

$a\perp b$表示$a$与$b$互质,即$gcd(a,b)=1$。

$a|b$表示$a$可以整除$b$,即$gcd(a,b)=a$。

$a\require{cancel}\bcancel{-}b$表示$a$无法整除$b$,即$gcd(a,b)\neq a$。

$a\%b$表示满足$a\equiv(\mod b)$的最小自然数$x$。

设$Fp(k,x)$为$x$中因子$k$的最大幂次,即满足$x=rk^n(n,r\in N)$的最大$n$。

设$Ext(k,x)$为$x$除去所有因子$k$后的值,即$Ext(k,x)=\frac{x}{k^{Fp(k,x)}}$。

定义完了之后,我们接着看着道题,其实我们就是要求$Ext(10,n!)\mod 10^k$的值。

不妨先来考虑$k=1$。

$\because 10^k=10=2\times 5$,且$2\perp 5$,且$2$和$5$都是质数(哇塞,性质好多~)。

求一个值对一个合数取模的值,而这个合数还是两个质数的积,所以考虑中国剩余定理($CRT$),用其求出上式对$2$和$5$取模的值。

$Ext(10,n!)\mod 2$的值很容易求,简单说一下怎么求。

$\because$在$n>1$的时候有$Fp(2,n!)>Fp(5,n!)$,又$\because 10=2\times 5$,$\therefore Fp(10,n!)=Fp(5,n!)$。

$$\begin{array}{ll} \therefore Ext(10,n!) &=& \dfrac{n!}{10^{Fp(10,n!)}} \\ &=& \dfrac{n!}{2^{Fp(10,n!)}5^{Fp(10,n)}} \\ &=& \dfrac{n!}{2^{Fp(5,n!)}5^{Fp(5,n!)}}\end{array}$$

$\therefore$分子$n!$中因子$2$的幂次大于分母上$2$的幂次,所以有:

$$Ext(10,n!)\equiv 0(\mod 2)(n>1)$$

那么我们就只要求$Ext(10,n!)\mod 5$的值就好了。

刚刚我们知道我们要求$\dfrac{n!}{2^{Fp(5,n!)}5^{Fp(5,n!)}}$,而分母中$s^{Fp(5,n!)}\perp 5$,$\therefore$其在$\mod 5$的意义下是有逆元的。

那么我们考虑如何快速求解$Fp(5,n!)$,由定义可得,$n!=\prod \limits_{k=1}^n k$,$\therefore$可以求出$[1,n]$中有因子$5$的数的个数$\sim$有因子$5^k$的数的个数,每一个$k$都能产生新的贡献,$\therefore$只要求和即为$Fp(5,n!)$,也就是说$Fp(5,n!)=\sum \limits_k\left\lfloor\frac{n}{5^k}\right\rfloor$,这样做的时间复杂度是$\Theta(\log_5 n)$的。

这部分的贡献我们先不着急,剩下的问题就等价于求$Ext(5,n!)$了。

但是显然我们还是不好求$Ext(5,n!)$,然而我们发现$Ext(k,x)$在$k$一定时是完全积性函数,即$Ext(k,a)\times Ext(k,b)=Ext(k,ab)$,给出简单证明:

$$\begin{array}{ll} Ext(k,ab) &=& \dfrac{ab}{k^{Fp(k,ab)}} \\ &=& \dfrac{ab}{k^{Fp(k,a)}k^{Fp(k,b)}} \\ &=& Ext(k,a)\times Ext(k,b)\end{array}$$

$\therefore$有:

$$Ext(5,n!)=\prod \limits_{k=1}^kExt(5,k)$$

又$\because Ext(k,k)=1$,$\therefore$根据上面的性质,还可以得到:

$$Ext(k,x)=Ext(k,\frac{x}{k}),k|x$$

利用上面的性质,继续推导:

$$\begin{array}{ll} Ext(5,n!) &=& \prod \limits_{k=1}^nExt(5,k) \\ &=& \prod \limits_{k\in [1,n],5|k}Ext(5,\frac{k}{5})\times \prod \limits_{k\in [1,n],5\require{cancel}\bcancel{-}k}Ext(5,k) \\ &=& Ext(5,(\frac{5}{n})!) \times \prod \limits_{k\in [1,n],5\require{cancel}\bcancel{-}k}Ext(5,k) \\ &=& Ext(5,(\frac{n}{5})!)\times \prod \limits_{k\in [1,n],5\require{cancel}\bcancel{-}k}k \end{array}$$

其中对于$5|k$是原问题的子问题,可以用递归求解。

而对于后面$5\require{cancel}\bcancel{-}k$的部分,由于我们要求的只是上式对$5$取模后的值,所以我们可以将$k$全部对$5$取模后分组,于是有:

$$\prod \limits_{k\in [1,n],5\require{cancel}\bcancel{-}k}k\equiv(\prod \limits_{k\in [1,5],5\require{cancel}\bcancel{-}k}k)^{\left\lfloor\frac{n}{5}\right\rfloor}\times \prod \limits_{k\in [1,n\mod 5],5\require{cancel}\bcancel{-}k}k(\mod 5)$$

前半部分是定值的若干次幂,直接快速幂求解即可,但是$\left\lfloor\frac{n}{5}\right\rfloor$我们需要降幂,可以考虑扩展欧拉定理,即对于任意$a,n,p\neq 0$,有$a^n\equiv a^{n\mod \phi(p)+\phi(p)}(\mod p)$。

后半部分预处理出来即可。

最后我们找回来我们一开始咕着的$2^{Fp(5,n!)}$的逆元,将上式乘上它在用$CRT$即可。

这样,我们对于$k=1$的求解就结束了~

下面考虑一般情况……

对于$k=1$的情况,我们考虑的是模数是$10$的情况,而对于$k=3$的情况,我们只需要想办法将其拓展到模数是$1000$就好了。

发现$1000=10^3=2^3\times 5^3$,$\therefore$我们可以用$2^3=8$和$5^3=125$作为$CRT$的剩余器即可。

$\because$发现$Fp(2,n!)$依然大于$Fp(5,n!)$,$\therefore$可以暂且将$Ext(10,n!)\mod 8$当作$0$来处理(只有$n$很小的时候才不能被$8$整除,但是直接暴力求解就好了,无非就是打一个数据点分治),那么现在的瓶颈就在于$\mod 125$意义下值的求解。

那么我们现在就是想办法拓展下式:

$$Ext(5,(\frac{n}{5})!)\times \prod \limits_{k\in [1,n],5\require{cancel}\bcancel{-}k}k$$

改变分组策略即可,即将组长改为$125$即可,于是上式变成了:

$$Ext(5,n!)\equiv Ext(5,(\frac{n}{5})!)\times (\prod \limits_{k\in [1,125],5\require{cancel}\bcancel{-}k}k)^{\left\lfloor\frac{n}{125}\right\rfloor}\times \prod \limits_{k\in [1,n\mod 125],5\require{cancel}\bcancel{-}k}k(\mod 125)$$

也可以这么看,设剩余器$p=5^k$,则有:

$$Ext(5,n!)\equiv Ext(5,(\frac{n}{5})!)\times (\prod \limits_{k\in [1,p],5\require{cancel}\bcancel{-}k}k)^{\left\lfloor\frac{n}{p}\right\rfloor}\times \prod \limits_{k\in [1,n\mod p],5\require{cancel}\bcancel{-}k}k(\mod p)$$

问题解决了,剩下的就看代码实现了,我一开始打了$242$行才$AC$的,可是对面$DeepinC$就打了$40$行,反正我是做不到了,能$A$就行……

时间复杂度:$\Theta(\log_5^n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
char ch[101];
int N[101],EXT[101],FLAG[101],NOW[101],wzc[101];
long long n;
int k;
int mod[]={1,10,100,1000};
int fiv[]={1,5,25,125};
int phi[]={1,4,20,100};
long long mzz[200],fac[200];
long long ans;
long long qpow(long long x,long long y,long long MOD)
{
long long res=1;
while(y)
{
if(y&1)res=res*x%MOD;
x=x*x%MOD;
y>>=1;
}
return res;
}
void pre_work()
{
fac[0]=1;
memset(EXT,0,sizeof(EXT));
}
void work()
{
ans=1;
for(long long i=1;i<=n;i++)
{
ans*=i;
while(!(ans%10))ans/=10;
ans%=mod[k];
}
}
void copy(){for(int i=0;i<=N[0];i++)FLAG[i]=N[i];}
void divideFLAG()
{
int jw=0;
for(int i=FLAG[0];i;i--)
{
jw*=10;
jw+=FLAG[i];
wzc[i]=jw/5;
jw%=5;
}
int len=0;
int top=FLAG[0];
for(int i=top;i;i--)
{
if(wzc[i])len=max(len,i);
FLAG[i]=wzc[i];
}
FLAG[0]=len;
}
void explus()
{
int jw=0;EXT[0]=max(EXT[0],FLAG[0]);
for(int i=1;i<=EXT[0];i++)
{
EXT[i]+=FLAG[i]+jw;
jw=EXT[i]/10;
EXT[i]%=10;
}
for(int i=EXT[0]+1;i;i++)
{
if(!jw)break;
EXT[i]++;
jw=EXT[i]/10;
EXT[i]%=10;
EXT[0]=i;
}
}
int ModEXT()
{
int res=0;
for(int i=EXT[0];i;i--)
{
res=res*10+EXT[i];
res%=phi[k];
}
return res;
}
void get()
{
int now=0;
memset(wzc,0,sizeof(wzc));
for(int i=N[0];i;i--)
{
now=now*10+N[i];
if(now<fiv[k])continue;
int pre=now;
now/=fiv[k];
now%=10;
wzc[i]=now;
now=pre-now*fiv[k];
}
int len=0;
int top=N[0];
for(int i=top;i;i--)
{
if(wzc[i])len=max(len,i);
NOW[i]=wzc[i];
}
NOW[0]=len;
}
int ModNOW()
{
int res=0;
for(int i=NOW[0];i;i--)
{
res=res*10+NOW[i];
res%=phi[k];
}
return res;
}
int ModN()
{
int res=0;
for(int i=N[0];i;i--)
{
res=res*10+N[i];
res%=fiv[k];
}
return res;
}
void divideN()
{
int jw=0;
for(int i=N[0];i;i--)
{
jw*=10;
jw+=N[i];
wzc[i]=jw/5;
jw%=5;
}
int len=0;
int top=N[0];
for(int i=top;i;i--)
{
if(wzc[i])len=max(len,i);
N[i]=wzc[i];
}
N[0]=len;
}
void ex_work()
{
reverse(N+1,N+N[0]+1);
EXT[0]=0;
copy();
while(FLAG[0])
{
divideFLAG();
explus();
}
long long inv=qpow(qpow(2,phi[k]-1,fiv[k]),ModEXT(),fiv[k]);
for(int i=1;i<mod[k];i++)
if(!(i%(mod[k]/fiv[k])))
mzz[i%fiv[k]]=i;
for(int i=1;i<fiv[k];i++)
{
fac[i]=fac[i-1];
if(i%5)fac[i]=fac[i]*i%fiv[k];
}
long long res=1;
while(N[0])
{
get();
res=res*qpow(fac[fiv[k]-1],ModNOW(),fiv[k])%fiv[k];
res=res*fac[ModN()]%fiv[k];
divideN();
}
res=res*inv%fiv[k];
ans=mzz[res];
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
pre_work();
scanf("%s%d",ch+1,&k);
N[0]=strlen(ch+1);
for(int i=1;i<=N[0];i++)N[i]=ch[i]-'0';
if(N[0]==1){n=N[1];work();}
else ex_work();
switch(k)
{
case 1:printf("%01lld\n",ans);break;
case 2:printf("%02lld\n",ans);break;
case 3:printf("%03lld\n",ans);break;
}
}
return 0;
}

rp++

[CSP-S模拟测试]:数字(数学+高精度)的更多相关文章

  1. 程序点滴001_Python模拟点阵数字

    尝试过很多编程语言,写过不少程序(当然,基本上都是些自娱自乐或给自己用的工具类的小玩意儿),逐渐认识到编写程序是一个不断完善.不断优化的过程——编程首先要有一个想法(目标),围绕这个目标形成最基本的功 ...

  2. 转 C#实现PID控制的模拟测试和曲线绘图

    C#实现PID控制的模拟测试和曲线绘图   本文分两部分,一部分是讲PID算法的实现,另一部分是讲如何用动态的曲线绘制出PID运算的结果. 首先,PID算法的理论模型请参考自动控制理论,最早出现的是模 ...

  3. [考试反思]0729NOIP模拟测试10

    安度因:哇哦. 安度因:谢谢你. 第三个rank1不知为什么就来了.迷之二连?也不知道哪里来的rp 连续两次考试数学都占了比较大的比重,所以我非常幸运的得以发挥我的优势(也许是优势吧,反正数学里基本没 ...

  4. PCB布线设计-模拟和数字布线的异同(转)

    工程领域中的数字设计人员和数字电路板设计专家在不断增加,这反映了行业的发展趋势.尽管对数字设计的重视带来了电子产品的重大发展,但仍然存在,而且还会一直存在一部分与模拟或现实环境接口的电路设计.模拟和数 ...

  5. csp-s模拟测试94

    csp-s模拟测试94 一场简单题,打爆了.$T1$脑抽分解质因数准备分子分母消,想了半天发现$jb$互质直接上天,果断码了高精滚蛋.$T2$无脑手玩大样例,突然灵光一闪想到映射到前$K$大小的区间, ...

  6. 「题解」NOIP模拟测试题解乱写II(36)

    毕竟考得太频繁了于是不可能每次考试都写题解.(我解释个什么劲啊又没有人看) 甚至有的题目都没有改掉.跑过来写题解一方面是总结,另一方面也是放松了. NOIP模拟测试36 T1字符 这题我完全懵逼了.就 ...

  7. 0823NOIP模拟测试赛后总结

    考了两场感觉虚了... NOIP模拟测试30 分着考的. 就只有T2的美妙的暴力拿分了,60分rank10,挂了. T1是一道sb题,爆零了十分遗憾. 许多人都掉进了输出格式的坑里,C没大写.少个空格 ...

  8. 2019.8.14 NOIP模拟测试21 反思总结

    模拟测试20的还没改完先咕着 各种细节问题=错失190pts T1大约三分钟搞出了式子,迅速码完,T2写了一半的时候怕最后被卡评测滚去交了,然后右端点没有初始化为n…但是这样还有80pts,而我后来还 ...

  9. 2019.8.1 NOIP模拟测试11 反思总结

    延迟了一天来补一个反思总结 急匆匆赶回来考试,我们这边大家的状态都稍微有一点差,不过最后的成绩总体来看好像还不错XD 其实这次拿分的大都是暴力[?],除了某些专注于某道题的人以及远程爆踩我们的某学车神 ...

随机推荐

  1. 打印流PrintWriter

    * 打印流 * 字节流打印流 PrintStream * 字符流打印流PrintWriter * * 打印流的特点: * A:只有写数据的,没有读取数据,只能操作目的地,不能操作数据源 * * B:可 ...

  2. 删除历史日志的一个API

    删除历史日志的一个API bool DeleteOldFiles(const char* strFolder, const char* strPrefix, bool is_recursion, UI ...

  3. 07 oracle 归档模式 inactive/current redo log损坏修复--以及错误ORA-00600: internal error code, arguments: [2663], [0], [9710724], [0], [9711142], [], [], [], [], [], [], []

    07 oracle 归档模式 inactive/current redo log损坏修复--以及错误ORA-00600: internal error code, arguments: [2663], ...

  4. 设置HTML中字体的粗细

    设置font-weight 属性:normal : 默认值.正常的字体.相当于 400 .声明此值将取消之前任何设置bold : 粗体.相当于 700 .也相当于 b 对象的作用bolder : 比 ...

  5. Appium+Python之异常自动截图

    运行过程中出现异常情况,我们怎么直观的看到呢?最简单的方法就是可以把异常现象截图下来. 思路:我这里采用get_screenshot_as_file(filename)方法,filename通过获取时 ...

  6. django信号相关

    Django中提供了“信号调度”,用于在框架执行操作时解耦.通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者. 1.Django内置信号 Model signals pre_in ...

  7. 提升JAVA代码的好“味道”

    让代码性能更高 需要 Map 的主键和取值时,应该迭代 entrySet() 当循环中只需要 Map 的主键时,迭代 keySet() 是正确的.但是,当需要主键和取值时,迭代 entrySet() ...

  8. 省电的iPhone定位

    1.Getting the User’s Current Location 获取用户当前位置. 获取位置的方式有三种:GPS, cell tower triangulation(蜂窝站点), 和 Wi ...

  9. eclipse 代码提示快捷键 alt+/

    eclipse (ALT+/)1.选择Eclipse菜单栏中的Window->preferences: 2.选择General->keys; 3.在右侧中间的窗体中点击word compl ...

  10. 基于Redis实现简单的分布式锁【理论】

    摘要 分布式锁在很多应用场景下是非常有效的手段,比如当运行在多个机器上的不同进程需要访问同一个竞争资源的时候,那么就会涉及到进程对资源的加锁和释放,这样才能保证数据的安全访问.分布式锁实现的方案有很多 ...