余数(数论分块)

题目描述:

 

题解:

首先容易想到:当 i > n 时,n mod i = n

所以如果 m > n , 

  ans+=((m-n)%mod)*(n%mod)%mod;

  m=n;

接下来考虑 i<=n 的情况:

后面这个东西很明显可以用数论分块算,时间复杂度是O(sqrt(N))

但我当时做的时候还没有学数论分块,所以代码十分丑陋

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll n,m,ans;
ll l,r,h,t,cnt,sum;
int main(){
    scanf("%lld%lld",&n,&m);
    if(n<=m){
        ans+=((m-n)%mod)*(n%mod)%mod;
        ll sq=sqrt(n);
        for(ll i=1;i<=n/(sq+1);i++)
            ans=(ans+(n%i))%mod;//这一区间内的n%i没有特殊规律,只能硬算
        for(ll i=sq;i>0;i--){//此处i表示除完以后向下取整的结果 
            l=n/(i+1)+1;r=n/i;//用n除以[l,r]区间内的数取整完结果均为i
            //统计 n mod 这些数的余数就是求等差数列:n-i*l,n-i*(l+1),...,n-i*r 的和  
            h=n-l*i,t=n-r*i;//首项,末项 
            cnt=r-l+1,sum=h+t;//项数,首项加末项的和 
            if(cnt&1ll)sum>>=1;
            else cnt>>=1;//除以2 
            if(l<=r) ans=(ans+(cnt%mod)*(sum%mod))%mod;
        } 
    }
    else{
        ll sq=sqrt(n);//基本同上 
        if(m<=(n/(sq+1))){
            for(ll i=1;i<=m;i++)
                ans=(ans+(n%i))%mod;
        }
        else{
            for(ll i=1;i<=n/(sq+1);i++)
                ans=(ans+(n%i))%mod;
            for(ll i=sq;i>0;i--){
                l=n/(i+1)+1;r=n/i;
                if(r>=m) r=m;
                h=n-l*i,t=n-r*i;
                cnt=r-l+1,sum=h+t;
                if(cnt&1ll)sum>>=1;
                else cnt>>=1;
                if(l<=r) ans=(ans+(cnt%mod)*(sum%mod))%mod;
                if(r==m) break;
            }
        }
    }
    printf("%lld",ans);
    return 0;
}
posted @ 2019-08-19 16:51  Mistletoes  阅读(403)  评论(0编辑  收藏  举报