bzoj 2154: Crash的数字表格

Crash在数学课上学习了最小公倍数,并尝试解决一个关于N*M数字表格的问题,该表格的每个格子里填写的是其行列坐标的最小公倍数。本题涉及数论推导和高效算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2154: Crash的数字表格

Time Limit: 20 Sec   Memory Limit: 259 MB
Submit: 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

4 5

Sample Output

122
【数据规模和约定】
100%的数据满足N, M ≤ 107。

HINT

Source

[ Submit][ Status][ Discuss]


题解:数论神题!!


式子推导完毕后,可以发现函数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);
} 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值