题目
求,
Div2:n<=1e7
Div1:n<=1e10
思路来源
Div2:https://blog.csdn.net/Eternally831143/article/details/86755286(反演)
Div1:https://blog.csdn.net/Anoy_acer/article/details/86654825(杜教筛)
题解1(莫比乌斯反演 约数变倍数)
,
枚举d,,
计,
,则gcd(i,j)可取,有
同乘μ反演一下,有
另一方面,gcd(i,j)是d的倍数,只需i是d的倍数,j是d的倍数,这样的数只有
有,有,,
由,
先数论分块求g(d),对于所有相同的部分相同,[l,r]区间求和
然后数论分块求ans,对于所有相同的部分,
令,这段区间中每个,显然相同,[l,r]区间求和,求得ans
后记:发现还是直接用e=mu*1(*为卷积)硬拆方便,
无脑拆,枚举k,找k的倍数
题解2(杜教筛 构造欧拉函数前缀和)
最后的答案,
,是因为P只计入gcd(i,j)中i>=j部分,乘2一并计入j>=i部分,-1减去i==j部分
先欧拉筛预处理前1e7项,作前缀和,做杜教筛的查表部分
对ans分块,求出所有n/i贡献相同的[l,r]部分,其贡献为F(n/i)*(mu[r]-mu[l-1]),求和即可
djsmu和djsphi由于常用,就当做板子吧,推导可以用构造推导
心得
主要要会推导式子,数论分块要熟练
入门了之后,反演和杜教筛就是刷题找感觉了吧
代码1(n<=1e7)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
const int maxn=1e7+10;
bool ok[maxn];
int prime[maxn],mu[maxn],cnt;
ll n,ans;
void sieve()
{
mu[1]=1;
for(ll i=2;i<maxn;++i)
{
if(!ok[i])
{
prime[cnt++]=i;
mu[i]=-1;
}
for(int j=0;j<cnt;++j)
{
if(i*prime[j]>=maxn)break;
ok[i*prime[j]]=1;
if(i%prime[j]==0)
{
mu[i*prime[j]]=0;//如果开的是全局,就不用管
break;
}
else mu[i*prime[j]]=-mu[i];
}
}
for(int i=2;i<maxn;++i)
mu[i]=mu[i-1]+mu[i];//mu前缀和
}
ll g(ll d)
{
ll ans=0;
for(int l=1,r;l<=n/d;l=r+1)
{
r=n/(n/l);
ans=ans+1ll*(mu[r]-mu[l-1]+mod)*(n/d/l)%mod*(n/d/l)%mod;
if(ans>=mod)ans-=mod;
if(ans<0)ans+=mod;
}
return ans;
}
int main()
{
sieve();
scanf("%lld",&n);
for(int l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans+=1ll*(mu[r]-mu[l-1]+mod)%mod*g(l)%mod;
if(ans>=mod)ans-=mod;
if(ans<0)ans+=mod;
}
printf("%lld\n",ans);
return 0;
}
代码2(n<=1e10)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
const int mod=998244353;
const int maxn=1e7+10;
bool ok[maxn];
int prime[maxn],mu[maxn],cnt;
ll phi[maxn];
map<ll,int>smu;//mu的前缀和
map<ll,ll>sphi;//phi的前缀和
void sieve()
{
phi[1]=mu[1]=1;
for(ll i=2;i<maxn;++i)
{
if(!ok[i])
{
prime[cnt++]=i;
phi[i]=i-1;
mu[i]=-1;
}
for(int j=0;j<cnt;++j)
{
ll k=i*prime[j];
if(k>=maxn)break;
ok[k]=1;
if(i%prime[j]==0)
{
phi[k]=phi[i]*prime[j];
mu[k]=0;
break;
}
else
{
phi[k]=phi[i]*(prime[j]-1);
mu[k]=-mu[i];
}
}
}
for(int i=1;i<maxn;++i)//搞前缀和
{
mu[i]=mu[i-1]+mu[i];
phi[i]=phi[i-1]+phi[i];
}
}
int djsmu(ll x)//求mu前缀和
{
if(x<maxn)return mu[x];
if(smu[x])return smu[x];
int ans=1;
for(ll l=2,r;l<=x;l=r+1)
{
r=x/(x/l);
ans-=1ll*(r-l+1)*djsmu(x/l);
}
return smu[x]=ans;
}
ll djsphi(ll x) //求phi 前缀和
{
if(x<maxn)return phi[x]%mod;
if(sphi[x])return sphi[x];
ll ans;//x*(x+1)/2
if(x%2==0)ans=(x/2%mod)*((x+1)%mod)%mod;
else ans=((x+1)/2)%mod*(x%mod)%mod;
for(ll l=2,r;l<=x;l=r+1)
{
r=x/(x/l);
ans=(ans-(r-l+1)%mod*djsphi(x/l)%mod);
if(ans>=mod)ans-=mod;
if(ans<0)ans+=mod;
}
return sphi[x]=ans;
}
ll n,ans;
int main()
{
sieve();
scanf("%lld",&n);
for(ll l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans+=(djsmu(r)-djsmu(l-1)+mod)%mod*(djsphi(n/l)*2-1+mod)%mod;
if(ans>=mod)ans-=mod;
if(ans<0)ans+=mod;
}
printf("%lld\n",ans);
return 0;
}