题面:
输入:
一行三个数,n, m, k。
输出:
数据范围:
Solution:
遇到这种题首先考虑莫比乌斯反演(谁叫有gcd呢)遇到这种题首先考虑莫比乌斯反演(谁叫有gcd呢)
先写出式子:Ans=∑ni=1∑mj=1gcd(i,j)k先写出式子:Ans=∑i=1n∑j=1mgcd(i,j)k
尝试化简:先假设n<=m,将枚举i,j变为枚举gcd,现在问题转变为:尝试化简:先假设n<=m,将枚举i,j变为枚举gcd,现在问题转变为:
Ans=∑nd=1dk∑ni=1∑mj=1[gcd(i,j)==d]Ans=∑d=1ndk∑i=1n∑j=1m[gcd(i,j)==d]
事实上后面这个东西莫比乌斯反演基础题吧,不多讲了,设函数F(d)=∑ni=1∑mj=1[d|gcd(i,j)]=⌊nd⌋⌊md⌋事实上后面这个东西莫比乌斯反演基础题吧,不多讲了,设函数F(d)=∑i=1n∑j=1m[d|gcd(i,j)]=⌊nd⌋⌊md⌋
然后反演即可,那么我们可得到式子:Ans=∑nd=1dk∑d|iμ(id)⌊nd⌋⌊md⌋然后反演即可,那么我们可得到式子:Ans=∑d=1ndk∑d|iμ(id)⌊nd⌋⌊md⌋
改变枚举:Ans=∑nd=1dk∑⌊nd⌋i=1μ(i)⌊nid⌋⌊mid⌋改变枚举:Ans=∑d=1ndk∑i=1⌊nd⌋μ(i)⌊nid⌋⌊mid⌋
此时枚举的⌊nd⌋可以数论分块,对于右边的因为已知ndmd,所以仍然可以数论分块.此时枚举的⌊nd⌋可以数论分块,对于右边的因为已知ndmd,所以仍然可以数论分块.
我们两次分块,再用线性筛预处理莫比乌斯函数,就可以把复杂度降为O(n)了.我们两次分块,再用线性筛预处理莫比乌斯函数,就可以把复杂度降为O(n)了.
此时其实我们可以继续化简,设T=id,枚举T:此时其实我们可以继续化简,设T=id,枚举T:
Ans=∑nT=1⌊nT⌋⌊mT⌋∑d|Tdkμ(Td)Ans=∑T=1n⌊nT⌋⌊mT⌋∑d|Tdkμ(Td)
可以发现后面这一部分如果把dk替换成函数,那么是狄利克雷卷积的形式,根据定理:可以发现后面这一部分如果把dk替换成函数,那么是狄利克雷卷积的形式,根据定理:
两个积性函数的狄利克雷卷积也是积性函数:我们就可以把每个T对应的值用线性筛预处理出来两个积性函数的狄利克雷卷积也是积性函数:我们就可以把每个T对应的值用线性筛预处理出来
这样就可以应对多组数据,每次O(n−−√)复杂度了这样就可以应对多组数据,每次O(n)复杂度了
至于那个积性函数的预处理,具体看代码吧至于那个积性函数的预处理,具体看代码吧
代码:
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 5e6 + 5;
typedef long long ll;
# define mod 1000000007
ll prime[N],sum[N],f[N],g[N];
bool is[N];
int n,m,k,tot;
ll ans;
inline ll pow(ll x,int p)
{
ll sum = 1;
while (p)
{
if (p & 1) sum = (sum * x) % mod;
x = (x * x) % mod; p >>= 1;
}
return sum;
}
inline void pre()
{
f[1] = 1;
for (int i = 2;i <= N - 5; ++i)
{
if (!is[i]) { g[i] = pow(i * 1ll,k); f[i] = g[i] >= 1 ? g[i] - 1 : g[i] - 1 + mod; prime[++tot] = i; }
for (int j = 1;j <= tot && prime[j] * i <= N - 5; ++j)
{
is[prime[j] * i] = 1;
if (i % prime[j] == 0)
{
f[prime[j] * i] = (f[i] * g[prime[j]]) % mod;
break;
}
f[prime[j] * i] = (f[i] * f[prime[j]]) % mod;
}
}
for (int i = 1;i <= N - 5; ++i) sum[i] = (sum[i - 1] + f[i]) % mod;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
pre();
if (n > m) swap(n,m);
for (int l = 1,r; l <= n; l = r + 1)
{
r = min(n,min(n / (n / l),m / (m / l)));
ans = (ans + (n / l) * 1ll * (m / l) % mod * 1ll * (sum[r] - sum[l - 1] + mod)) % mod;
}
printf("%lld\n",ans);
return 0;
}