题目链接:http://codeforces.com/contest/616/problem/E
题目大意:给定整数n,m(1≤n,m≤1013), 求(n mod 1 + n mod 2 + ... + n mod m)的值(mod Pt = 1e9 + 7)。
思路:这题一看是看觉得题意简洁,通过人数不多一定是一道用到各种定理的碉堡数论题。后来仔细想了一下发现是乱搞…
首先通过观察数据范围,结合数论题的复杂度传统考虑O(√n)算法。
把n拆解,可以任意写成很多种n = px + r 的形式,而p, x中必有一个≤√n,首先对于[1,√n]的x暴力处理n % x,而当x > √n的时候,就出现了一个[1, √n]的p,对应一段连续的x的情况,同样r也是对应的。如果能够快速批处理出这些r的和,那么就能通过1 - √n枚举p来搞定x∈[√n, +∞]的情况。通过观察,对于任何一个p对应的连续的x区间,r都成首项为n - (n / p) * p, 公差为p,项数为n / p - n / (p + 1)的等差数列前n项和,因此带入公式可O(1)得解。 先枚举p去做代码比较好些,直到n / p < √n,把成块的x处理结束,剩下谁就直接从1到那个值暴力枚举计算比较容易写。
当m > n的时候把m变成n,答案初始为n * (m - n) 即可。值得注意的是m < n的时候,要注意从p等于多少开始能覆盖到m,那个边界的x区间中,r的首项为n % m。
题目要求% Pt, 这一点卡的很严,注意n % m之后还要再 % Pt,而且中间计算的过程中每一步运算都要%,防止爆long long。
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 typedef long long LL; 8 9 const LL Pt = 1e9 + 7; 10 LL n, m, d1, d2, a, k, ans, pos, ans1; 11 12 int main() 13 { 14 scanf("%I64d%I64d", &n, &m); 15 if (m > n) ans = ((n % Pt) * ((m - n) % Pt)) % Pt, m = n; 16 bool flag = false; 17 for (LL i = 2; i <= n; i++) 18 { 19 d1 = n / i; d2 = n / (i - 1); 20 if (d1 < m && !flag) 21 { 22 flag = true; 23 a = (n % m) % Pt; 24 k = (m - d1) % Pt; 25 a = ((a * k) % Pt + (((k * (k - 1) / 2) % Pt) * (i - 1)) % Pt) % Pt; 26 ans = (ans + a) % Pt; 27 continue; 28 } 29 if (flag) 30 { 31 a = (n - d2 * (i - 1)) % Pt; 32 k = (d2 - d1) % Pt; 33 a = ((a * k) % Pt + (((k * (k - 1) / 2) % Pt) * (i - 1)) % Pt) % Pt; 34 ans = (ans + a) % Pt; 35 } 36 if (d1 <= sqrt(n)) {pos = d1; break;} 37 } 38 for (LL i = 1; i <= min(pos, m); i++) ans = (ans + n % i) % Pt; 39 printf("%I64d\n", ans); 40 }