BZOJ-2190 [SDOI2008]仪仗队(莫比乌斯反演)
题目描述
在一个平面直角坐标系的以 \((0,0)\) 为左下角、\((n,n)(1\leq n\leq 40000)\) 为右上角的矩形中,除了 \((0,0)\) 之外,每个坐标上都插着一个钉子。
求在原点向四周看去,能够看到多少个钉子。一个钉子能被看到,当且仅当连接它和原点的线段上没有其他钉子。
分析
答案为 两个坐标轴 和 一个面 两部分(两个坐标轴对答案的贡献为 \(2\))。
\[ans=2+\sum_{i=1}^{n-1}\sum_{j=1}^{n-1}[\gcd(i,j)=1]
\]
则:
\[\begin{aligned}&\sum_{i=1}^{n}\sum_{j=1}^{n}[\gcd(i,j)=1]\\
=&\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{d\mid \gcd(i,j)}\mu(d)\\
=&\sum_{d=1}^{n}\mu(d)\sum_{i=1}^{n}\sum_{j=1}^{n}[d\mid\gcd(i,j)]\\
=&\sum_{d=1}^{n}\mu(d)\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor \frac{n}{d}\rfloor} 1\\
=&\sum_{d=1}^{n}\mu(d)\Big\lfloor\frac{n}{d}\Big\rfloor^2
\end{aligned}
\]
时间复杂度 \(O(\sqrt{n})\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000010;
bool vis[maxn];
long long n,cnt,prime[maxn],mu[maxn],sum[maxn];
void init()
{
mu[1]=1;
for(int i=2;i<maxn;i++)
{
if(!vis[i])
{
prime[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt&&i*prime[j]<maxn;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)
break;
else
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<maxn;i++)
sum[i]=sum[i-1]+mu[i];
}
long long solve(long long n)
{
long long ans=2;
for(long long l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans=ans+(sum[r]-sum[l-1])*(n/l)*(n/l);
}
return ans;
}
int main()
{
init();
int n;
cin>>n;
cout<<solve(n-1)<<endl;
return 0;
}
posted on 2020-11-14 17:59 DestinHistoire 阅读(88) 评论(0) 收藏 举报
浙公网安备 33010602011771号