题目
给定N,M求出(N%1+N%2+N%3+N%4+…+N%M)的和,由于答案很大,最后答案要对(10^9+7)取余
数据范围
1≤N,M≤1013 1 ≤ N , M ≤ 10 13
分析
首先,看到这数据范围内心感受就是凉了……
然后就开始茫茫然然地寻找规律。。
我们令其中的被除数N为A,模数为i,余数为Q_i,我们可以写出:
A≡Qi(mod i)
A
≡
Q
i
(
m
o
d
i
)
因为编程除法正数是向下取整,所以我们也可以写成:
A=A/i∗i+Qi
A
=
A
/
i
∗
i
+
Q
i
’那我们其实就是求:
(Q1+Q2+Q3+...+QM)%MOD
(
Q
1
+
Q
2
+
Q
3
+
.
.
.
+
Q
M
)
%
M
O
D
的和
又因为单个Q_i我们可以表示成:
Qi=A−A/i∗i
Q
i
=
A
−
A
/
i
∗
i
那么我们令该和为S,则:
S=(A−A/1∗1)+(A−A/2∗2)+(A−A/3∗3)+...+(A−A/M∗M)
S
=
(
A
−
A
/
1
∗
1
)
+
(
A
−
A
/
2
∗
2
)
+
(
A
−
A
/
3
∗
3
)
+
.
.
.
+
(
A
−
A
/
M
∗
M
)
化简一下就成了:
S=A∗M−(A/1∗1+A/2∗2+A/3∗3+...+A/M∗M)
S
=
A
∗
M
−
(
A
/
1
∗
1
+
A
/
2
∗
2
+
A
/
3
∗
3
+
.
.
.
+
A
/
M
∗
M
)
考试时化简到这里就懵逼了,于是就没做起…
但其实我们稍稍打一下关于A/i表就可以发现:
(这里N=40,M=40)
这里A/i整体呈越靠后越密集,也就是,相等的区间长度整体看来越来越长并且A/i不相等的只有11个,那我们就可以用枚举A/i的思路
我们可以发现,A/i算出的位置出现在以A/i为区间结尾的最后一个,那么我们就能得出一个A/i的区间范围:
A/i:[L,R][A/(i+1)+1,A/i]
A
/
i
:
[
L
,
R
]
[
A
/
(
i
+
1
)
+
1
,
A
/
i
]
我们又可以发现整个区间里面的数又是连续的,于是高斯求和(首项加末项乘项数除以2~)就可以用了那我们把L设为A/(i+1)+1,R设为A/i,那么这一段A/i值相等的数连续的数就可以算出:
i∗(L+R)∗(R−L+1)2
i
∗
(
L
+
R
)
∗
(
R
−
L
+
1
)
2
于是你就可以将i从1枚举到log(n)(其实你再枚举多一点也没问题),因为剩下的(1~所剩下最大的数)可以直接枚举了,要注意一点,当R大于m时要把R置为m
当你把i从小到大枚举时,[L,R]整个区间是不断向左靠的
最后温馨提示:模运算要跟上!!
代码
#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
LL read(){
LL f=1,x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
return x*f;
}
#define MAXN 1000
#define INF 0x3f3f3f3f
#define MOD int((1e9)+7)
int main(){
LL n=read(),m=read(),tmp=0,ans=(n%MOD)*(m%MOD)%MOD;
LL ln=(LL)sqrt(n),Les;
m=min(n,m);//因为当m>n时没有必要,ans只会减0
for(LL i=1;i<=ln;i++){
LL l=n/(i+1)+1,r=n/i;//枚举区间A/i
r=min(r,m);//r不能大于m
if(l>r) continue;//说明这个区间要么没有数,要么r>m取了min后不合法
LL s1=l+r,s2=r-l+1;//高斯函数~
if(s1%2==0) s1/=2;
else s2/=2;
tmp=(tmp+(s1%MOD)*(s2%MOD)%MOD*i%MOD)%MOD;
}
Les=min(m,n/(ln+1));//剩下的数
ans=(ans+MOD-tmp)%MOD;
for(int i=1;i<=Les;i++){
tmp=n/i%MOD*i%MOD;
ans=(ans+MOD-tmp)%MOD;
}
printf("%lld\n",ans);
return 0;
}