[CSP-S模拟测试]:123567(莫比乌斯函数+杜教筛+数论分块)

题目传送门(内部题92)


输入格式

  一个整数$n$。


输出格式

  一个答案$ans$。


样例

样例输入:

13

样例输出:

9


数据范围与提示

  对于$20\%$的数据,$n\leqslant 10^6$。
  对于$40\%$的数据,$n\leqslant 10^{12}$。
  对于$100\%$的数据,$0\leqslant n\leqslant 10^{18}$。


题解

这道题里的小$\mu$其实就提示我们了$\mu$,也就是莫比乌斯函数。

那么,我们可以列出式子:

$$ans=\sum \limits_{i=1}^\sqrt{n}\mu(i)\left\lfloor\frac{n}{i^2}\right\rfloor$$

但是这个式子还是不足矣$AC$,考虑优化。

先考虑对于一段内的$\left\lfloor\frac{n}{i^2}\right\rfloor$是相等的,想到数论分块,对于一个区间$[l,r]$,满足$\left\lfloor\frac{n}{l^2}\right\rfloor=\left\lfloor\frac{n}{r^2}\right\rfloor$,那么现在就需要想办法快速求出每一段的$\sum \limits_{i=l}^r\mu(i)$。

很快想到杜教筛,然后这道题就没了……

时间复杂度:$\Theta(n^{\frac{2}{5}})$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
map<int,int>mp;
long long n;
long long ans;
bool vis[30000001];
int prime[30000001],mu[30000001],cnt;
void pre_work()
{
	mu[1]=1;
	for(int i=2;i<=30000000;i++)
	{
		if(!vis[i])
		{
			prime[cnt++]=i;
			mu[i]=-1;
		}
		for(int j=0;j<cnt&&i*prime[j]<=30000000;j++)
		{
			vis[i*prime[j]]=1;
			if(i%prime[j])mu[i*prime[j]]=-mu[i];
			else{mu[i*prime[j]]=0;break;}
		}
	}
}
int get(int x)
{
	if(x<=30000000)return mu[x];
	if(mp[x])return mp[x];
	int res=1;
	for(int i=2,j;i<=x;i=j+1)
	{
		j=x/(x/i);
		res-=get(x/i)*(j-i+1);
	}
	return mp[x]=res;
}
int main()
{
	pre_work();scanf("%lld",&n);
	for(int i=1;i<=30000000;i++)mu[i]+=mu[i-1];
	for(long long i=1,j;i*i<=n;i=j+1)
	{
		j=sqrt(n/(n/(i*i)));
		ans+=(n/(i*i))*(get(j)-get(i-1));
	}
	printf("%lld",ans);
	return 0;
}

rp++

posted @ 2019-10-27 20:38  HEOI-动动  阅读(277)  评论(0编辑  收藏  举报