题目要求输出 从 1 ~ n 中 所有 m 的倍数的个位数之和。
n很大, 暴力肯定不行,而但凡是数,都有一定的规律性,
一个数的倍数又有什么规律呢?
以 3 为例,
1倍就是一个3, 两倍就是 两个3相加, 三倍就是 三个 3 相加,直到十倍, 以十倍为一个循环,十一倍时又是一个新的循环的开始,
3 6 9 12 15 18 21 24 27 30 | 33 36 39 42 45 48 51 54 57 60 .....
很容易看出每相隔 30 ,3的所有倍数的个位数之和都是一个定值, 3 + 6 + 9 + 2 ....+...+ 7
所以我们只需要求出 n 中有几个30 然后 在乘以 3 + 6 + 9 + 2 ....+...+ 7,就先算出了1 ~ (n / 30)* 30 的答案。
最后, 加上 n - (n/30) * 30 中 3 的倍数的个位数之和就OK了。
代码:
#include <iostream>
#include <string>
using namespace std;
#define ll unsigned long long
ll f(ll m,ll &q){
ll sum = 0;
for(ll i = m,j = 2; i%10!=0;j++ ){ // 将 m 分别与 1 ~ 9 相乘, 记录 m ~ m*9 中不同个位的和.
sum+=i%10;
i = m * j;
q = i;
}
return sum;
}
int main(){
int T;
cin>>T;
while(T--){
ll m;ll n;
cin>>n;
cin>>m;
// 剪枝不加这一步会超时,当m为10的倍数, m的所有倍数的个位都是0,直接输出0
if(m % 10 == 0|| n < m) {cout<<"0"<<endl; continue;}
if(n == m) {cout<<n%10<<endl; continue;} //当n==m 直接输出n%10
ll sum = 0, q, p; // q保存当m的倍数的个位数再次重复出现时的第一个值。则 每 一个 q , 都含有
p = f(m,q); // p保存从m~q的所有个位不同的倍数的数量。
ll y = n / q; // 则 n / q 是 n 中 一共有几个 q, n不能为10的倍数,否则此处会除以0!
sum = y * p; //每隔 q,m的不同个位的倍数之和都为p 所以 sum = y * p;
n = n - y *q; //然后算剩下的数
if(n){
for(ll i = m,j = 2; i<=n;j++){
sum+=i%10;
i = m*j;
}
}
cout<<sum<<endl;
}
return 0;
}
本文介绍了一种高效算法,用于计算从1到n范围内所有m的倍数的个位数字之和,避免了暴力计算的复杂度。通过发现倍数的周期性规律,将问题分解为完整周期和剩余部分的计算,显著提高了计算效率。

被折叠的 条评论
为什么被折叠?



