BZOJ2154:Crash的数字表格
BZOJ2693:jzptab
BZOJ 2154
给 n,m ,求 ∑i=1n∑j=1n[lcm(i,j)]将原式适当变形得:
∑i=1n∑j=1n[lcm(i,j)]====∑i=1n∑j=1ni⋅jgcd(i,j)∑d∑i=1⌊nd⌋∑i=1⌊md⌋d2⋅i⋅jd[gcd(i,j)==1]∑dd∑i=1⌊nd⌋∑i=1⌊md⌋i⋅j⋅∑x|i∧x|jμ(x)∑dd∑xμ(x)⋅x2⋅⌊ndx⌋⋅(⌊ndx⌋+1)2⋅⌊mdx⌋⋅(⌊mdx⌋+1)2
有了上述公式,通过预处理 μ(x)⋅x2 前缀和便可做到 O(n√⋅n√)=O(n) 的单次询问优秀的复杂度。
BZOJ2693
给多组n,m,求 ∑i=1n∑j=1n[lcm(i,j)]明显,再使用刚才的算法会达到 O(n2) 的复杂度,无法接受。
不妨设
D=dx
.
再次变形得
不难发现,如果求出 F(D)=∑x|DDi⋅μ(x)⋅x2 的前缀和,单次询问将会在 O(n√) 时间内完成。
对于
F(x)
函数,可以通过两种方式实现。
1.枚举法。对于每一个约数枚举其倍数。根据调和数列的性质可以知道该方法预处理时间复杂度为
O(nlogn)
。
2.线筛法。
首先有一个结论。
积性函数的约数和为积性函数
因为积性函数的卷积也是积性函数。
此外,当
D=d1∗q,q为d1约数
时
枚举
x
,当
对于所有有意义的数,其
Dx
项必然扩大
q
倍,
其余的情况都可以转化而来,便得到了线性筛法。
- Code 2693
#include<bits/stdc++.h>
using namespace std;
const int Maxn=1e7;
const int Mod=1e8+9;
typedef long long ll;
inline int read()
{
char ch=getchar();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
return i*f;
}
int Prime[Maxn+50],pr[Maxn+50],tot;
ll F[Maxn+50];
inline void sieve()
{
F[1]=1;
for(ll i=2;i<=Maxn;i++)
{
if(!pr[i])
{
pr[i]=i;
F[i]=(i-(i*i%Mod))%Mod;
Prime[++tot]=i;
}
for(int j=1;j<=tot;j++)
{
int k=i*Prime[j];
if(k>Maxn)break;
pr[k]=Prime[j];
if(i%Prime[j]==0)
{
F[k]=F[i]*Prime[j]%Mod;
break;
}
F[k]=F[i]*F[Prime[j]]%Mod;
}
}
for(int i=1;i<=Maxn;i++)(F[i]+=F[i-1])%=Mod;
}
inline int sum(int x,int y)
{
return (1ll*x*(x+1)/2)%Mod*((1ll*y*(y+1)/2)%Mod)%Mod;
}
int main()
{
sieve();
int T=read();
while(T--)
{
int n=read(),m=read();
if(n>m)swap(n,m);
int pos,ans=0;
for(int bg=1;bg<=n;bg=pos+1)
{
pos=min(n/(n/bg),m/(m/bg));
ans=(1ll*ans+1ll*sum(n/bg,m/bg)*(F[pos]-F[bg-1])%Mod)%Mod;
}
printf("%d\n",(ans+Mod)%Mod);
}
}