[国家集训队]Crash的数字表格 / JZPTAB
Posted asuldb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[国家集训队]Crash的数字表格 / JZPTAB相关的知识,希望对你有一定的参考价值。
这道题我们要求的是
[sum_{i=1}^Nsum_{j=1}^Mlcm(i,j)]
总所周知(lcm)的性质不如(gcd)优雅,但是唯一分解定理告诉我们(gcd(i,j) imes lcm(i,j)=i imes j)
所以很容易的可以转化成这个柿子
[sum_{i=1}^Nsum_{j=1}^Mfrac{i imes j}{(i,j)}]
现在开始套路了
先设两个函数
[f(n)=sum_{i=1}^Nsum_{j=1}^M[(i,j)==n] imes i imes j]
[F(n)=sum_{i=1}^Nsum_{j=1}^M[n|(i,j)] imes i imes j]
[=frac{n imes(left lfloor frac{N}{n} ight floor+1) imes left lfloor frac{N}{n} ight floor}{2} imesfrac{n imes(left lfloor frac{M}{n} ight floor+1) imes left lfloor frac{M}{n} ight floor}{2}]
显然则有
[F(n)=sum_{n|d}f(d)]
反演得
[f(n)=sum_{n|d}mu(frac{d}{n})F(d)]
于是答案就是
[Ans=sum_{i=1}^Nfrac{f(i)}{i}]
[=sum_{i=1}^N imesfrac{1}{i}sum_{i|d}mu(frac{d}{i})frac{d imes(left lfloor frac{N}{d} ight floor+1) imes left lfloor frac{N}{d} ight floor}{2} imesfrac{d imes(left lfloor frac{M}{d} ight floor+1) imes left lfloor frac{M}{d} ight floor}{2}]
后面的一大坨东西真是太烦人了,搞到前面来
[Ans=sum_{d=1}^Nfrac{(left lfloor frac{N}{d} ight floor+1) imes left lfloor frac{N}{d} ight floor imes(left lfloor frac{M}{d} ight floor+1) imes left lfloor frac{M}{d} ight floor}{4}sum_{i|d}frac{mu(frac{d}{i}) imes d^2}{i}]
于是我们可以用(Theta(n ln n))来求出(sum_{i|d}frac{mu(frac{d}{i}) imes d^2}{i})之后前缀和
于是有了一个(70)分的代码
#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define maxn 10000005
#define LL long long
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
const LL mod=20101009;
int n,m;
int f[maxn],p[maxn>>2];
LL pre[maxn];
int main()
{
scanf("%d%d",&n,&m);
if(n>m) std::swap(n,m);
f[1]=pre[1]=1;
for(re int i=2;i<=n;i++)
{
if(!f[i]) p[++p[0]]=i,pre[i]=(1-i+mod);
for(re int j=1;j<=p[0]&&p[j]*i<=n;j++)
{
f[p[j]*i]=1;
if(i%p[j]==0)
{
pre[i*p[j]]=pre[i];
break;
}
pre[p[j]*i]=pre[p[j]]*pre[i]%mod;
}
}
for(re int i=1;i<=n;i++) pre[i]=(i*pre[i]%mod+pre[i-1])%mod;
LL ans=0;
for(re LL l=1,r;l<=n;l=r+1)
{
r=min(n/(n/l),m/(m/l));
LL N=n/l,M=m/l;
ans=(ans+((N+1)*N/2%mod)*((M+1)*M/2%mod)%mod*(pre[r]-pre[l-1]+mod)%mod)%mod;
}
printf("%lld
",ans);
return 0;
}
显然不行
我们设(T=frac{d}{i})
那么
[sum_{i|d}frac{mu(frac{d}{i}) imes d^2}{i}=d imessum_{T|d}mu(T) imes T]
定义(h(x)=sum_{T|x}mu(T) imes T)
会发现(h)是一个积性函数,可以考虑如何线筛
首先(x)是质数(h(x)=1-x)
互质的话可以直接乘起来
如果不互质的话需要好好考虑一下了
仔细思考一下这个(h)的含义,会发现有一些约数(T)是没有用的,就是那些(mu(T)=0)的约数
而线筛的时候一旦(i\%p[j]==0),说明(p[j])在(i)中出现过,于是(p[j])并不能组成一些新的有用约数,函数值和(h(i))相比其实没有什么变化,所以就有
[h(p[j]*i)=h(i)]
于是现在的答案变成了
[Ans=sum_{d=1}^Nfrac{(left lfloor frac{N}{d} ight floor+1) imes left lfloor frac{N}{d} ight floor imes(left lfloor frac{M}{d} ight floor+1) imes left lfloor frac{M}{d} ight floor}{4} imes d imes h(d)]
于是直接求(d imes h(d))的前缀和就好了
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define maxn 10000005
#define LL long long
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
const LL mod=20101009;
int n,m;
int f[maxn],p[maxn>>2];
LL pre[maxn];
int main()
{
scanf("%d%d",&n,&m);
if(n>m) std::swap(n,m);
f[1]=pre[1]=1;
for(re int i=2;i<=n;i++)
{
if(!f[i]) p[++p[0]]=i,pre[i]=(1-i+mod);
for(re int j=1;j<=p[0]&&p[j]*i<=n;j++)
{
f[p[j]*i]=1;
if(i%p[j]==0)
{
pre[i*p[j]]=pre[i];
break;
}
pre[p[j]*i]=pre[p[j]]*pre[i];
}
}
for(re int i=1;i<=n;i++) pre[i]=(i*pre[i]%mod+pre[i-1])%mod;
LL ans=0;
for(re LL l=1,r;l<=n;l=r+1)
{
r=min(n/(n/l),m/(m/l));
LL N=n/l,M=m/l;
ans=(ans+((N+1)*N/2%mod)*((M+1)*M/2%mod)%mod*(pre[r]-pre[l-1]+mod)%mod)%mod;
}
printf("%lld
",ans);
return 0;
}
以上是关于[国家集训队]Crash的数字表格 / JZPTAB的主要内容,如果未能解决你的问题,请参考以下文章
P1829 [国家集训队]Crash的数字表格 / JZPTAB