题目
题目链接:http://codeforces.com/problemset/problem/616/E
题目来源:cf educational round 5 E
简要题意:求 (∑i=1mnmodi)mod109+7
数据范围: 1⩽n,m⩽1013
题解
比较有意思的数论题,可惜一处粗心没在场上A掉,适合对拍检验。
做法是分块,然后对每块里面用等差数列求和公式去求和。
首先定义一些变量,以下所有除为整数除,不保留余数:
be:当前块的开始位置,最开始由1开始,下一个块的起始位置为上一块结束+1 en:当前块结束的位置
mul:对[be,en]之间的数x均有n/x=mul
于是我们有以下等式求出所有变量:
mul=n/be en=min(m,n/mul)(不能超过m)
若我们将
[be,en] 之间的数表示为 be+c 由于先前的定义,我们有:n/(be+c)=mul
nmod(be+c)=n−(n/(be+c))×(be+c)=n−mul⋅(be+c)
可以看出来是正比的,然后用个等差数列求和公式去算即可。
m>n 的情况把后边的直接乘出来即可因为此时每次结果都为 n <script type="math/tex" id="MathJax-Element-17">n</script>。
实现
许多红名哥的做法是先加了再去减,这个做法相对而言比我好,因为避免了一些溢出的问题。
求等差的时候要除,同时直接乘出来再除会有溢出,因此要用逆元,直接逆元超时,要提出来。
代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
LL powmod(LL a,LL b, LL MOD) {LL res=1;a%=MOD;for(;b;b>>=1){if(b&1)res=res*a%MOD;a=a*a%MOD;}return res;}
// head
const LL MOD = 1e9+7;
LL ck(LL n, LL m) {
LL ans = 0;
for (int i = 1; i <= m; i++) {
ans += (n%i);
ans %= MOD;
}
return ans;
}
LL solve(LL n, LL m) {
LL be = 1, en, mul, ans = 0;
while (be <= m) {
mul = n/be;
en = min(n/mul, m);
LL cur = n%be;
LL num = (MOD+en-be)%MOD;
ans += (MOD + cur + cur-num*mul%MOD) % MOD * (num+1) % MOD;
ans %= MOD;
be = en+1;
}
return ans * powmod(2, MOD-2, MOD) % MOD;
}
int main()
{
LL n, m;
scanf("%I64d%I64d", &n, &m);
LL ans = 0;
if (m > n) {
ans = (MOD+m-n)%MOD * (n%MOD) % MOD;
}
ans = ans+solve(n, min(m, n));
printf("%I64d\n", ans%MOD);
return 0;
}