牛客练习赛72D——brz的函数(莫比乌斯反演)

令所求的式子为

\(f(n)=\Sigma_i\Sigma_j\mu(ij)\)

则可以写出如下递推式,画个 \(n*n\) 的表格可辅助理解

\(f(n)=f(n-1)+2\Sigma_{x=1}^n\mu(xn)\)

考虑 \(\Sigma_{x=1}^n\mu(xn)\) 的含义,这里先直接给出结果:

\(\Sigma_{x=1}^n\mu(xn)=\Sigma_{x=1}^n[gcd(x,n)=1]*\mu(x)*\mu(n)\)

上式的含义最简洁的一种理解方式是,由于莫比乌斯函数是积性的,因此当二者gcd为1时,有 \(\mu(xn)=\mu(x)\mu(n)\);而不为1的时候 \(xn\) 一定有平方因子,因此乘起来整个都是0。因此,可以写成上式。

使用经典结论 \([dog=1]=\Sigma_{d|dog}\mu(d)\) 可以继续得到:

\(\Sigma_{x=1}^n\mu(xn)=\Sigma_{x=1}^n[gcd(x,n)=1]*\mu(x)*\mu(n)\)

\(=\mu(n)\Sigma_{x=1}^n\mu(x)\Sigma_{d|gcd(x,n)}\mu(d)\)

交换求和次序

\(=\Sigma_{d|n}\mu(d)*\Sigma_{d|x,x\leq n}\mu(x)\)

此时似乎很难往下化简了,我也一度陷入了懵逼当中,但当我冷静下来时,我发现化简到这一步已经够用了,因为上式直接计算似乎是(好吧我瞎猜的,应该差不多)个 \(O(\sqrt{n}*log(n))\) 的复杂度,所以就直接计算就好了。

这样,总的程序复杂度就是 \(O(n\sqrt{n}*log(n))\) ,可以写了。

具体实现的时候,要预处理出每个数的所有因数,并且要在发现某 \(\mu\) 值为零时立刻continue剪个枝才能通过此题。

代码:

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(x) ((int)(x).size())
#define fastin ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef pair<int,int> pii;
typedef double db;

const int N=2e5+5;
int tot,flg[N+5],p[N+5],mu[N+5];
void getMu() {
	mu[1] = 1;
	for (int i = 2; i <= N; ++i) {
		if (!flg[i]) p[++tot] = i, mu[i] = -1;
		for (int j = 1; j <= tot && i * p[j] <= N; ++j) {
			flg[i * p[j]] = 1;
			if (i % p[j] == 0) {
				mu[i * p[j]] = 0;
				break;
			}
			mu[i * p[j]] = -mu[i];
		}
	}
}
int ans[50010],t,n;
vector<int> fac[50010];
int main(){
	getMu();
	ans[1]=1;
	rep(i,1,50000){
		for(int j=i;j<=50000;j+=i){
			fac[j].pb(i);
		}
	}
	rep(i,2,50000){
		if(!mu[i]){
			ans[i]=ans[i-1];continue;
		}
		int cof=2*mu[i];
		int sum=0;
		for(auto d:fac[i]){
			if(!mu[d])	continue;
			int tmp=0;
			for(int x=d;x<i;x+=d){
				tmp+=mu[x];
			}
			tmp*=mu[d];
			sum+=tmp;
		}
		ans[i]=ans[i-1]+cof*sum;
	}
	cin>>t;
	while(t--){
		cin>>n;
		printf("%d\n",ans[n]);
	}
	return 0;
}
posted @ 2020-11-06 23:13  炸鸡块君  阅读(122)  评论(0编辑  收藏  举报