C.idol!!

数学

题目大意

正整数 nn 的双阶乘 n!!n!! 表示不超过 nn 且与 nn 有相同奇偶性的所有正整数乘积 求对于给定 nni=1ni!!\prod\limits_{i=1}^n i!! 的后缀 00 个数

解题思路

蒟蒻一枚//只能用最原始的思考方法qwq稍微有那么亿些复杂


根据双阶乘的性质,可以得到: (n1)!!×n!!=n!(n-1)!!\times n!!=n!

因此对于给定的 nn ,原式可化为:

i=1ni!!={i=1n2(2i)!,ni=1n+12(2i1)!,n\prod\limits_{i=1}^n i!!= \begin{cases} \prod\limits_{i=1}^\frac{n}{2} (2i)! &,n为偶数 \\ \prod\limits_{i=1}^\frac{n+1}{2} (2i-1)! &,n为奇数 \end{cases}

显而易见的,阶乘中因子 22 的个数一定多于因子 55 的个数,因此题目等价于求上式中因子 55 的个数//


考虑某单一阶乘 n!n! 中所含因子 55 的个数。

可以发现,每个 55 的倍数项会提供 11 个因子 55 ,共有 n5\lfloor \dfrac{n}{5} \rfloor 项 除此之外每个 25=5225=5^2 的倍数项会额外提供一个因子 55 ,共有 n52\lfloor \dfrac{n}{5^2} \rfloor

再除此之外每个 125=53125=5^3 的倍数项会额外提供一个因子 55 ,共有 n53\lfloor \dfrac{n}{5^3} \rfloor 项……

因此对于单一阶乘 n!n! ,其提供因子 55 的数量 cnt5=i=1Nn5i(5N>n)cnt_5=\sum\limits_{i=1}^N \lfloor \dfrac{n}{5^i} \rfloor (5^N>n)


接着考虑连乘积中因子 55 个数的总和。

ans={i=1n2j=1N2i5j=i=1Nj=1n22j5i,ni=1n+12j=1N2i15j=i=1Nj=1n+122j15i,nans=\begin{cases} \sum\limits_{i=1}^\frac{n}{2} \sum\limits_{j=1}^N \lfloor \dfrac{2i}{5^j} \rfloor=\sum\limits_{i=1}^N \sum\limits_{j=1}^\frac{n}{2} \lfloor \dfrac{2j}{5^i} \rfloor &,n为偶数 \\ \sum\limits_{i=1}^\frac{n+1}{2} \sum\limits_{j=1}^N \lfloor \dfrac{2i-1}{5^j} \rfloor=\sum\limits_{i=1}^N \sum\limits_{j=1}^\frac{n+1}{2} \lfloor \dfrac{2j-1}{5^i} \rfloor &,n为奇数 \end{cases} \\

对于某一 ii ,发现不论 nn 的奇偶, j=1j=1 开始的每 5i5^i 项之和构成公差为 2×5i2\times5^i 的等差数列//

例:i=1i=1nn 为偶数且足够大时,2j5i\lfloor \dfrac{2j}{5^i} \rfloor 的前 1515 项如下,其中每 55 项之和构成公差为 5×25\times 2 的等差数列: 0,0,1,1,22,2,3,3,44,4,5,5,60,0,1,1,2||2,2,3,3,4||4,4,5,5,6……

经计算,对于某一 ii ,等差数列的首项为

a1={5i2+2,n5i2+1,na_1=\begin{cases} \lfloor \dfrac{5^i}{2} \rfloor+2 &,n为偶数 \\ \lfloor \dfrac{5^i}{2} \rfloor+1 &,n为奇数 \end{cases}

完整的段用等差数列求和,非完整的段手算一下//

若此前完整段的数量记为 mm ,则:

非完整段前 5i2\lfloor \dfrac{5^i}{2} \rfloor 项的值为 2m2m

5i2+1\lfloor \dfrac{5^i}{2} \rfloor+12×5i22\times\lfloor \dfrac{5^i}{2} \rfloor 项的值为 2m+12m+1(手搓一下就知道了),

求和即可

N=5n+1N=\lfloor \log_5n \rfloor+1 ,对 i[1,N]i\in[1,N] 遍历求和得到答案

由于答案数据极其庞大,超出了C++ %lld(64bits)的范围,因此需要使用更高位数的整数类型(如int128)//或者直接转战Python

时间复杂度

O(logn)O(\log n)

参考代码

import math
# while 1:
n=int(input())
N=int(math.log(n,5)+1)
re=0
if n%2==0 :
    for i in range(1,N+1) :
        #print("i="+str(i))
        a1=(5**i)//2+2 #首项
        #print("a1="+str(a1))
        d=(5**i)*2 #公差
        #print("d="+str(d))
        m=(n//2)//(5**i) #完整段数
        #print("m="+str(m))
        re+=(2*a1+(m-1)*d)*m//2 #完整段等差数列求和
        #print("re1:" + str(re))
        re+=(n//2-m*(5**i))*2*m #最后一段余项求和
        #print("re2:" + str(re))
        #print("pl1=" + str((n//2-m*(5**i))*2*m))
        if n//2-m*(5**i)>(5**i)//2 :
            re+=n//2-m*(5**i)-(5**i)//2
            #print("pl2=" + str(n//2-m*(5**i)-(5**i)//2))
                

if n%2 :
    for i in range(1,N+1) :
        #print("i="+str(i))
        a1=(5**i)//2+1 #首项
        #print("a1="+str(a1))
        d=(5**i)*2 #公差
        #print("d="+str(d))
        m=((n+1)//2)//(5**i) #完整段数
        #print("m="+str(m))
        re+=(2*a1+(m-1)*d)*m//2 #完整段等差数列求和
        #print("re1:" + str(re))
        re+=((n+1)//2-m*(5**i))*2*m #最后一段余项求和
        #print("re2:" + str(re)) 
        #print("pl1=" + str(((n+1)//2-m*(5**i))*2*m))
        if (n+1)//2-m*(5**i)>(5**i)//2 :
            re+=(n+1)//2-m*(5**i)-(5**i)//2
            #print("pl2=" + str((n+1)//2-m*(5**i)-(5**i)//2))
    
print(re)