2154: Crash的数字表格
Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 2255 Solved: 844
[ Submit][ Status][ Discuss]
Description
今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数a和b,LCM(a, b)表示能同时被a和b整除的最小正整数。例如,LCM(6, 8) = 24。回到家后,Crash还在想着课上学的东西,为了研究最小公倍数,他画了一张N*M的表格。每个格子里写了一个数字,其中第i行第j列的那个格子里写着数为LCM(i, j)。一个4*5的表格如下: 1 2 3 4 5 2 2 6 4 10 3 6 3 12 15 4 4 12 4 20 看着这个表格,Crash想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当N和M很大时,Crash就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash只想知道表格里所有数的和mod 20101009的值。
Input
输入的第一行包含两个正整数,分别表示N和M。
Output
输出一个正整数,表示表格中所有数的和mod 20101009的值。
Sample Input
Sample Output
【数据规模和约定】
100%的数据满足N, M ≤ 107。
HINT
Source
题解:数论神题!!
式子推导完毕后,可以发现函数s(x,y)可以预处理出mu[d]*d^2的前缀和,然后在O(sqrt(n)+sqrt(m))的时间内求解。
而经过推导原式也可以在o(sqrt(n)+sqrt(m))的时间内求解,那么总的时间复杂度接近o(n)
有几点需要注意:刚开始写的时候全用得long long ,结果内存炸了
然后就是long long 越界,int 与long long 之间的转化问题
还有就是mu需要多少就预处理多少,刚开始全都处理了10000000,结果TLE了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define N 10000000
#define mod 20101009
using namespace std;
int mu[10000003],prime[10000003],p[10000003];
long long s[10000003],n,m,t;
void calc()
{
mu[1]=1;
for (int i=2;i<=t;i++)
{
if (!p[i])
{
mu[i]=-1;
prime[++prime[0]]=i;
}
for (int j=1;j<=prime[0];j++)
{
if (i*prime[j]>t) break;
p[i*prime[j]]=1;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
else mu[i*prime[j]]=-mu[i];
}
}
s[0]=0;
for (ll i=1;i<=t;i++)
s[i]=(s[i-1]+(i*i*mu[i])%mod)%mod;
}
ll get(ll x,ll y)
{
ll ans1=(long long)((x+1)*x/2)%mod;
ll ans2=(long long)((y+1)*y/2)%mod;
return (ll)ans1*ans2%mod;
}
int main()
{
scanf("%lld%lld",&n,&m);
t=min(n,m);
calc();
ll jj=0; ll sum=0;
for (ll i=1;i<=t;i=jj+1)
{
jj=min(n/(n/i),m/(m/i));
ll ans=0;
ll x=n/i; ll y=m/i; ll tt=min(x,y); ll k=0;
for (ll j=1;j<=tt;j=k+1)
{
k=min(y/(y/j),x/(x/j));
ll xx=x/j; ll yy=y/j;
ans=(ans+((s[k]-s[j-1])%mod*get(xx,yy))%mod)%mod;
}
sum=(sum+(((i+jj)*(jj-i+1)/2)%mod*ans%mod)%mod)%mod;
}
printf("%lld\n",(sum+mod)%mod);
}