#include <iostream>
#include <cstdio>
using namespace std;
long long n,k;
inline long long getsum(long long l,long long r)
{
return (l+r)*(r-l+1)/2;
}
int main()
{
scanf("%lld%lld",&n,&k);
long long ans=0;
for(long long l=1;l<=n&&k/l>0;) // 如果 k/l=0,那么会 RE,仔细想想,这个块肯定是对答案没贡献的,所以直接跳出循环
{
long long r=min(k/(k/l),n); // 记得取 min
ans+=getsum(l,r)*(k/l);
l=r+1;
}
printf("%lld\n",k*n-ans);
return 0;
}
现在考虑求解 i=1∑ni2⌊in⌋⌊im⌋,容易发现,⌊in⌋⌊im⌋ 是单调不增的,所以可以使用数论分块,左端点为 l 的块右端点就是 min(⎣⎢⎢⎢⌊ln⌋n⎦⎥⎥⎥,⎣⎢⎢⎢⌊lm⌋m⎦⎥⎥⎥),i2 的和则可以使用公式来求解。
代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const long long mod=19940417,inv2=9970209,inv6=3323403;
long long n,m;
inline long long G(long long n,long long k)
{
long long res=0;
for(long long l=1;l<=n&&k/l>0;)
{
long long r=min(k/(k/l),n);
res+=(l+r)*(r-l+1)%mod*inv2%mod*(k/l)%mod;
res%=mod;
l=r+1;
}
return res;
}
inline long long F(long long n)
{
return (((n*n%mod-G(n,n))%mod)+mod)%mod;
}
inline long long sum(long long x) // 小学奥数的知识:1^2+2^2+3^2+...+x^2
{
return x*(x+1)%mod*(x+x+1)%mod*inv6%mod;
}
int main()
{
scanf("%lld%lld",&n,&m);
if(n>m)
{
long long t=n;
n=m;
m=t;
}
long long ans=F(n)*F(m)%mod-n*n%mod*m%mod+n*G(n,m)%mod+m*G(n,n)%mod; // 疯狂取模,少一个可能就会 WA
ans=(ans%mod+mod)%mod;
for(long long l=1;l<=n;)
{
long long r=min(n/(n/l),m/(m/l));
ans-=((sum(r)-sum(l-1))%mod+mod)%mod*(n/l)%mod*(m/l)%mod;
ans=(ans%mod+mod)%mod;
l=r+1;
}
printf("%lld\n",ans);
return 0;
}