- 题目
- 数据范围
- 分析
- 代码
题目
给定N,M求出(N%1+N%2+N%3+N%4+…+N%M)的和,由于答案很大,最后答案要对(10^9+7)取余
数据范围
1≤N,M≤10131≤N,M≤1013
分析
首先,看到这数据范围内心感受就是凉了……
然后就开始茫茫然然地寻找规律。。
我们令其中的被除数N为A,模数为i,余数为Q_i,我们可以写出:
A≡Qi(mod i)A≡Qi(modi)
因为编程除法正数是向下取整,所以我们也可以写成:
A=A/i?i+QiA=A/i?i+Qi’那我们其实就是求:
(Q1+Q2+Q3+...+QM)%MOD(Q1+Q2+Q3+...+QM)%MOD的和
又因为单个Q_i我们可以表示成:
Qi=A?A/i?iQi=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)2i?(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只会减0for(LL i=1;i<=ln;i++){LL l=n/(i+1)+1,r=n/i;//枚举区间A/ir=min(r,m);//r不能大于mif(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;
}