一道普及组题目
大家好,我是普及组选手,我又开考就上厕所切了一道普及组题
题意
给你两个整数 \(n,m\),求 \((\sum\limits_{i=1}^m n\space\text{mod}\space i)\mod (10^9+7)\)。
\(n,m\le 10^{13}\)
sol
小学生都猜得出来是整除分块裸题
然后发现对于整除分块的每一块,它们除 \(n\) 后下取整的值都相同,所以它们除 \(n\) 的余数是一个等差数列,公差就是它们除 \(n\) 后下取整的值的相反数
显然小学生都会等差数列求和,所以整除分块套个等差数列求和公式就做完了
注意特殊处理 最后一块残缺(即 \(m\) 在某个块中间) 以及 \(n\gt m\) 的情况。
考后发现爆成 \(70\) 分了
然后我就暴躁地找了一下问题,发现我取模是这么写的(只取了一行为例)
ans = (ans + 1ll * (n%i+n%j) % mod * (j-i+1) % mod * invmod % mod) % mod;
显然我注意到了乘数都高达 \(1e13\),所以从第一个乘数开始就取模。
但是我忘了给后面的乘数取模了……于是 \(1e9\times 1e13\) 就爆 long long 了……
zbl
#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
#define invmod 500000004
using namespace std;
inline ll read(){
ll x=0; bool f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
if(f) return x;
return 0-x;
}
ll n,m,ans;
int main(){
n=read(), m=read();
ll to=min(n,m),i,j;
for(i=1,j; i<=n; i=j+1){
j=n/(n/i);
if(j>to) break;
ans = (ans + 1ll * (n%i+n%j) % mod * ((j-i+1)%mod) % mod * invmod % mod) % mod;
}
if(i<=to) ans = (ans + 1ll * (n%i+n%m) % mod * ((m-i+1)%mod) % mod * invmod % mod) % mod;
if(m>n) ans = (ans + 1ll * n % mod * ((m-n)%mod) % mod) % mod;
cout<<ans<<endl;
return 0;
}
当然这题还有小学组方法,但我刚上幼儿园并没想到
嗯。。。