Sum of Remainders(CodeForces-616E)(数学推导规律)

题目

给定N,M求出(N%1+N%2+N%3+N%4+…+N%M)的和,由于答案很大,最后答案要对(10^9+7)取余

数据范围

1N,M1013 1 ≤ N , M ≤ 10 13

分析

首先,看到这数据范围内心感受就是凉了……
然后就开始茫茫然然地寻找规律。。
我们令其中的被除数N为A,模数为i,余数为Q_i,我们可以写出:
AQi(mod i) A ≡ Q i ( m o d   i )
因为编程除法正数是向下取整,所以我们也可以写成:
A=A/ii+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=AA/ii Q i = A − A / i ∗ i
那么我们令该和为S,则:
S=(AA/11)+(AA/22)+(AA/33)+...+(AA/MM) S = ( A − A / 1 ∗ 1 ) + ( A − A / 2 ∗ 2 ) + ( A − A / 3 ∗ 3 ) + . . . + ( A − A / M ∗ M )
化简一下就成了:
S=AM(A/11+A/22+A/33+...+A/MM) 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)(RL+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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值