C.idol!!
数学
题目大意
正整数 的双阶乘 表示不超过 且与 有相同奇偶性的所有正整数乘积 求对于给定 , 的后缀 个数
解题思路
蒟蒻一枚//只能用最原始的思考方法qwq稍微有那么亿些复杂
根据双阶乘的性质,可以得到:
因此对于给定的 ,原式可化为:
显而易见的,阶乘中因子 的个数一定多于因子 的个数,因此题目等价于求上式中因子 的个数//
考虑某单一阶乘 中所含因子 的个数。
可以发现,每个 的倍数项会提供 个因子 ,共有 项 除此之外每个 的倍数项会额外提供一个因子 ,共有 项
再除此之外每个 的倍数项会额外提供一个因子 ,共有 项……
因此对于单一阶乘 ,其提供因子 的数量
接着考虑连乘积中因子 个数的总和。
对于某一 ,发现不论 的奇偶, 开始的每 项之和构成公差为 的等差数列//
例: , 为偶数且足够大时, 的前 项如下,其中每 项之和构成公差为 的等差数列:
经计算,对于某一 ,等差数列的首项为
完整的段用等差数列求和,非完整的段手算一下//
若此前完整段的数量记为 ,则:
非完整段前 项的值为 ,
第 至 项的值为 (手搓一下就知道了),
求和即可
令 ,对 遍历求和得到答案
由于答案数据极其庞大,超出了C++ %lld(64bits)的范围,因此需要使用更高位数的整数类型(如int128)//或者直接转战Python
时间复杂度
参考代码
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)