1. 程式人生 > 實用技巧 >P1829 [國家集訓隊]Crash的數字表格 / JZPTAB

P1829 [國家集訓隊]Crash的數字表格 / JZPTAB

P1829 [國家集訓隊]Crash的數字表格 / JZPTAB

>題目連結<

為了書寫方便,本文中涉及的除法除特殊說明以外均向下取整

題目描述

今天的數學課上,Crash 小朋友學習了最小公倍數(Least Common Multiple)。對於兩個正整數 \(a\)\(b\)\(lcm(a,b)\) 表示能同時整除 \(a\)\(b\) 的最小正整數。例如,\(lcm(6,8)=24\)

回到家後,Crash 還在想著課上學的東西,為了研究最小公倍數,他畫了一張 \(n \times m\) 的表格。每個格子裡寫了一個數字,其中第 \(i\) 行第 \(j\) 列的那個格子裡寫著數為
\(lcm(i,j)\)

看著這個表格,Crash 想到了很多可以思考的問題。不過他最想解決的問題卻是一個十分簡單的問題:這個表格中所有數的和是多少。當 \(n\)\(m\) 很大時,Crash 就束手無策了,因此他找到了聰明的你用程式幫他解決這個問題。由於最終結果可能會很大,Crash 只想知道表格裡所有數的和 \(\bmod 20101009\) 的值。

輸入格式

輸入包含一行兩個整數,分別表示 \(n\)\(m\)

輸出格式

輸出一個正整數,表示表格中所有數的和 \(\bmod 20101009\)的值。

輸入輸出樣例

輸入

4 5

輸出

122

解析

題目求

\[\sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j) \]

不妨令 \(n\leq m\),正確性顯然。

由小學奧數,我們可知:\(lcm(i,j)=\frac{ij}{gcd(i,j)}\)

所以題目求的是

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

於是開始愉快(並不)推式子:令\(gcd(i,j)=d\)

\[\begin{aligned} & \sum_{i=1}^{n}\sum_{j=1}^{m}\frac{ij}{gcd(i,j)}\\ & =\sum_{d=1}^{n}\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=d]\frac{ij}{d}\\ & =\sum_{d=1}^{n}\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}[gcd(i,j)=1]\frac{id\cdot jd}{d}\\ & =\sum_{d=1}^{n}d\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}[gcd(i,j)=1]ij \end{aligned}\\ \]

後面這一團和GCD結論的形式很相似,我們可以依樣畫葫蘆推一下。

對於上界 \(n/d,m/d\) 不要想太多,可以當成 \(p,q\) 或其他變數推就是了。

\[\text{設:}f(x)=\sum_{i=1}^{p}\sum_{j=1}^{q}[gcd(i,j)=x]ij\\ F(x)=\sum_{x|d}^{p}f(d)\\ 由莫比烏斯反演得到:\\ f(x)=\sum_{x|d}\mu(\frac{d}{x})F(d)\\ f(1)=\sum_{i=1}^{p}\mu(i)F(i) \]

現在看 \(F(x)\) 如何求:

\[\begin{aligned} F(x) & =\sum_{x|d}^{p}f(d)\\ & =\sum_{x|d}^{p}\sum_{i=1}^{p}\sum_{j=1}^{q}[gcd(i,j)=d]ij\\ & =\sum_{i=1}^{p}\sum_{j=1}^{q}[x|gcd(i,j)]ij\\ & =x^2\sum_{i=1}^{p/x}\sum_{j=1}^{q/x}ij\\ & =x^2\sum_{i=1}^{p/x}i\cdot \frac{(1+\frac{q}{x})\frac{q}{x}}{2}\\ & =x^2\cdot \frac{(1+\frac{p}{x})\frac{p}{x}}{2}\cdot\frac{(1+\frac{q}{x})\frac{q}{x}}{2} \end{aligned} \]

代入莫比烏斯反演式子後:

\[Ans=\sum_{d=1}^{n}d\sum_{i=1}^{n/d}\mu(i)\cdot i^2\cdot\frac{(1+\frac{n}{id})\frac{n}{id}}{2}\cdot\frac{(1+\frac{m}{id})\frac{m}{id}}{2}\\ \text{令}g(n,m)=\sum_{i=1}^{n}\mu(i)\cdot i^2\cdot\frac{(1+\frac{n}{i})\frac{n}{i}}{2}\cdot\frac{(1+\frac{m}{i})\frac{m}{i}}{2}\\ Ans=\sum_{d=1}^{n}d\cdot g(\frac{n}{d},\frac{m}{d}) \]

對於 \(g(\frac{n}{d},\frac{m}{d})\) ,我們可以數論分塊求。

而對於整個式子,也是可以數論分塊的。

總複雜度 \(O(n+m)\)

code:(三年OI一場空,不開long long、unsigned long long見祖宗)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll N=1e7+10,mod=20101009;

int primes[N],tot=0;
ll mu[N],sum[N];
bool mp[N];

void init(int n)
{
	mu[1]=mp[1]=1;
	for(int i=2; i<=n; i++)
	{
		if(!mp[i]) primes[++tot]=i,mu[i]=-1;
		for(int j=1; i*primes[j]<=n; j++)
		{
			int x=primes[j]*i;
			mp[x]=1;
			if(i%primes[j]==0)
			{
				mu[x]=0;
				break;
			}
			mu[x]=-mu[i];
		}
	}

	for(int i=1; i<=n; i++)
		sum[i]=(sum[i-1]+1LL*i*i%mod*(mu[i]+mod))%mod;
}

inline ll func(ll n,ll m)
{
	return (1LL*(n+1)*n/2%mod)*1LL*((m+1)*m/2%mod)%mod;
}

ll solve(ll n,ll m)
{
	if(n>m) swap(n,m);
	ll res=0;
	for(int i=1,j=0; i<=n; i=j+1)
	{
		j=min(n/(n/i),m/(m/i));
		res=(res+1LL*(sum[j]-sum[i-1]+mod)*func(n/i,m/i)%mod)%mod;
	}
	return res;
}

int main()
{
	init(1e7);
	ll n,m;
	scanf("%lld%lld",&n,&m);
	if(n>m) swap(n,m);
	ll ans=0;
	for(int i=1,j=0; i<=n; i=j+1)
	{
		j=min(n/(n/i),m/(m/i));
		ans=(ll)(ans+1LL*(j-i+1)*(i+j)/2%mod*solve(n/i,m/i)%mod)%mod;
	}
	printf("%lld",ans);
	return 0;
}