【洛谷P1829】Crash的数字表格 / JZPTAB
题目
题目链接:https://www.luogu.com.cn/problem/P1829
今天的数学课上,Crash 小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数 \(a\) 和 \(b\),\(\text{lcm}(a,b)\) 表示能同时整除 \(a\) 和 \(b\) 的最小正整数。例如,\(\text{lcm}(6, 8) = 24\)。
回到家后,Crash 还在想着课上学的东西,为了研究最小公倍数,他画了一张 $ n \times m$ 的表格。每个格子里写了一个数字,其中第 \(i\) 行第 \(j\) 列的那个格子里写着数为 \(\text{lcm}(i, j)\)。
看着这个表格,Crash 想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当 \(n\) 和 \(m\) 很大时,Crash 就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash 只想知道表格里所有数的和 \(\bmod 20101009\) 的值。
思路
90pts
自己瞎推了一个 sb 式子,以为 \(O(n\log n)\) 可以卡过,结果 T 飞了 /kk。
直接暴力乱搞即可。时间复杂度 \(O(n\log n)\)。Link
100pts
显然有
设 \(f(n,m)=\sum^{n}_{i=1}\sum^{m}_{n=1}ij[\gcd(i,j)==1]\),那么
预处理 \(sum[i]=sum[i-1]+\mu(i)+i^2\),然后整除分块搞即可。
带回原式,\(ans=\sum^{\min(n,m)}_{d=1}d\times f(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor)\),依然可以整除分块。
时间复杂度 \(O(n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=10000010,MOD=20101009;
ll n,m,cnt,mu[N],prm[N],sum[N];
ll ans,pows[N];
bool v[N];
void findprm(ll n)
{
mu[1]=1;
for (ll i=2;i<=n;i++)
{
if (!v[i]) prm[++cnt]=i,mu[i]=-1;
for (ll j=1;j<=cnt;j++)
{
if (prm[j]>n/i) break;
v[prm[j]*i]=1; mu[prm[j]*i]=-mu[i];
if (!(i%prm[j]))
{
mu[prm[j]*i]=0;
break;
}
}
}
}
ll fpow(ll x,ll k)
{
ll ans=1;
for (;k;k>>=1,x=x*x%MOD)
if (k&1) ans=ans*x%MOD;
return ans;
}
ll solve(ll n,ll m)
{
ll ans=0;
for (ll l=1,r;l<=min(n,m);l=r+1)
{
r=min(n/(n/l),m/(m/l));
ans=(ans+(sum[r]-sum[l-1])*(n/l+1)%MOD*(m/l+1)%MOD*(n/l)%MOD*(m/l))%MOD;
}
return ans;
}
int main()
{
// freopen("data.in","r",stdin);
findprm(N-1);
for (ll i=1;i<N;i++)
sum[i]=(sum[i-1]+mu[i]*1LL*i*i)%MOD;
scanf("%lld%lld",&n,&m);
for (ll l=1,r;l<=min(n,m);l=r+1)
{
r=min(n/(n/l),m/(m/l));
ans=(ans+1LL*(l+r)*(r-l+1)/2LL%MOD*solve(n/l,m/l))%MOD;
}
printf("%lld",(ans%MOD+MOD)%MOD*fpow(4,MOD-2)%MOD);
return 0;
}