你最愿意做的哪件事,才是你的天赋所在

0%

HYSBZ-2818-莫比乌斯反演

题目链接

HYSBZ-2818

思路

这道题思路略微复杂,主要是需要交换枚举对象。
首先明确我们要求的答案

按照套路来
设f(n)为gcd(a,b)=n的个数
则F(n)为gcd(a,b)=n及n的倍数的个数
然后可以转化为

然后用莫比乌斯倍数反演

然后代入得到

然后我们交换枚举项,我们枚举

这时候我们枚举T=pt(T是中间变量,跟上面那个没有关系),只需要枚举到Min(n,m)

这一步可能很难理解,后面一部分,其实前面是倍数,那么相当于提取公因式后,乘上的数就变成因数了。
这个式子就是我们最终得到的式子了,后面的明显可以预处理出来,因为T确定了,后面的和式也就确定了
前面一部分我们可以用分块除法解决。
这题不要单独打表,会T。把get_mu放入while中即可,单组数据

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include<iostream>
#include<cstdio>
#include<cstring>
#include<assert.h>
using namespace std;
#define ll long long
const int maxnx = 1e7+10;
int mu[maxnx];
int sum[maxnx];
bool vis[maxnx];
int g[maxnx];
int prime[maxnx],cnt;
void get_mu(int maxn)
{
mu[1]=1;
for(int i=2;i<maxn;i++)
{
if(!vis[i])
{
prime[cnt++]=i;
mu[i]=-1;
}
for(int j=0;j<cnt&&prime[j]*i<maxn;j++)
{
vis[i*prime[j]]=true;
if(i%prime[j])
{
mu[i*prime[j]]=-mu[i];
}
else
{
mu[i*prime[j]]=0;
break;
}
}
}
for(int j=0;j<cnt;j++)
{
for(int i=1;i*prime[j]<maxn;i++)
{
g[i*prime[j]]+=mu[i];
}
}
for(int i=1;i<maxn;i++)sum[i]=sum[i-1]+g[i];
}
int main()
{
ll n;
while(scanf("%lld",&n)!=EOF)
{
ll ans = 0;
get_mu(n);
for(int i=1,last;i<=n;i=last+1)
{
last=n/(n/i);
ans+=1ll*(sum[last]-sum[i-1])*(n/i)*(n/i);
}
cout<<ans<<endl;
}
}
你最愿意做的哪件事,才是你的天赋所在
-------------你最愿意做的哪件事才是你的天赋所在-------------