蓝桥杯2022年第十三届省赛真题-因数平方和(中)
题目
法一、暴力枚举
-
一个f(n)有一个或多个约数,这一个或多个约数又是f(n)的倍数,直接统计i(从1到n)在f(1)...f(n)中出现了几次,这里可以归纳总结:每个i出现做为因子的个数是(n/i),最后全部取平方加起来则求到了题目要求的g(n),最后进行求余输出即可。
-
g(n)=(n/i)* i * i,其中i从1到n
mod = 1000000007
ans = 0
n = int(input())
for i in range(1, n+1):
ans += ((n//i)*i*i) % mod #n//i表示i的个数,*了两次i表示平方,
ans %= mod#ans 可能会累加大量的数值,进行取模运算,确保ans 的值不会超过机器的整数表示范围,导致计算结果不准确或溢出。
print(ans)
- 只能过30%的案例
法二、数学公式
- 上面我们得到了:g(n)=(n/i)ii,其中i从1到n
mod = 1000000007
am = 1000000007 * 6
ans = 0
# 输入n
n = int(input())
m = n
for i in range(1, n+1):
#可以举例子发现g(n)=n // i的平方和
m = n // i#每次计算1~m
temp = (((m * (m + 1)) % am) * (2 * m + 1)) % am#计算1加到m的公式
temp //= 6
ans += temp
ans %= mod
# 输出结果
print(ans)
法三、求逆元
乘法逆元
-
举个例子:(7/2)mod 5=>(7 * 1/2) mod 5=>(7 * 2^-1)mod 5;现在我们来找2的-1次方的逆元,就是找(多少2)mod 5=1,因为(23)mod 5=1,所以2和3在mod 5下互为“倒数”,这个倒数就叫乘法逆元。于是(7 * 2^-1)mod 5=>(7*3) mod 5=1。其中:乘法逆元只考虑取比mod 后数小的数;整数与模数互素才有乘法逆元。
-
幂运算求逆元
def qmi(a, b, p):#a,b 和 p,分别表示底数、幂数和模数。
res = 1#用于保存最终的结果
while b:
if b & 1:#检查 b 的二进制表示的最低位是否为 1
res = res * a % p#如果最低位为 1,表示当前需要乘上 a,则将 res 乘以 a 并对 p 取模,然后将结果赋值给 res。
a = a * a % p #更新 a 的值为 a 的平方对 p 取模
b >>= 1#将 b 右移一位,相当于将 b 的二进制表示向右移动一位,等价于整数除以 2
return res
mod = 1000000007
ni = qmi(6, mod-2, mod)#由费马定理推理得出传参为mod-2
ans = 0
n = int(input()) # 输入n
m = n
# 第一个循环,从1到10000
for i in range(1, 10001):
m = n // i # 计算m
if m == 0:
print(ans)
exit(0)
temp = ((m * (m + 1)) % mod) * (2 * m + 1) % mod # 计算临时变量temp
temp *= ni # 通过乘法逆元,原来公式中的/6转化为*ni
temp %= mod
ans += temp
ans %= mod
# 第二个循环,从10001到m
for i in range(1, m + 1):
ans += ((n // i - 10000) * i*i) % mod
ans %= mod
print(ans)
- 硬编码
mod = 1000000007
ni = 166666668 #为6mod1000000007的乘法逆元
ans = 0
n = int(input()) # 输入n
m = n
# 第一个循环,从1到10000
for i in range(1, 10001):
m = n // i # 计算m
if m == 0:
print(ans)
exit(0)
temp = ((m * (m + 1)) % mod) * (2 * m + 1)) % mod # 计算临时变量temp
temp *= ni #通过乘法逆元,原来公式中的/6转化为*ni
temp %= mod
ans += temp
ans %= mod
# 第二个循环,从10001到m
for i in range(1, m + 1):
ans += ((n // i - 10000) * i*i) % mod
ans %= mod
print(ans)