【洛谷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。

\[ans=\sum^{n}_{i=1}\sum^{m}_{j=1}\frac{ij}{\gcd(i,j)} \]

\[=\sum^{\min(n,m)}_{i=1}\frac{\sum^{\min(n,m)}_{i|d}\mu(\frac{d}{i})\times (d+\lfloor\frac{n}{d}\rfloor\times d)\times (d+\lfloor\frac{m}{d}\rfloor\times d)}{i} \]

直接暴力乱搞即可。时间复杂度 \(O(n\log n)\)Link

100pts

显然有

\[ans=\sum^{n}_{d=1}\times \sum^{\lfloor\frac{n}{d}\rfloor}_{i=1}\sum^{\lfloor\frac{m}{d}\rfloor}_{n=1}ij[\gcd(i,j)==1] \]

\(f(n,m)=\sum^{n}_{i=1}\sum^{m}_{n=1}ij[\gcd(i,j)==1]\),那么

\[f(n,m)=\sum^{\min(n,m)}_{d=1}\frac{(d+\lfloor\frac{n}{d}\rfloor\times d)\times \lfloor\frac{n}{d}\rfloor}{2}\times \frac{(d+\lfloor\frac{m}{d}\rfloor\times d)\times \lfloor\frac{m}{d}\rfloor}{2}\times \mu(d) \]

\[=\frac{\sum^{\min(n,m)}_{d=1}d^2\mu(d)(\lfloor\frac{n}{d}\rfloor+1)(\lfloor\frac{m}{d}\rfloor+1)\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor}{4} \]

预处理 \(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;
}
posted @ 2020-10-03 20:15  stoorz  阅读(126)  评论(0编辑  收藏  举报