#莫比乌斯函数,Miller-Rabin#洛谷 3653 小清新数学题

题目

(sum_{i=l}^rmu(i),r-lleq 10^5,1leq lleq rleq 10^{18})


分析

其实有一道可以算是弱化版的题目
这种类型的tricks就是枚举质数然后将([l,r])中该质数的倍数算莫比乌斯函数(比如说)
但这道题恰恰恶心在无法将所有在根号内的质数都算出来,
考虑如果([l,r])中存在一个数按照刚才的方法还剩下一个完全平方数,那么莫比乌斯函数为0,
如果按照刚才的方法还剩下一个质数,那就得用Miller-Rabin判断,符号取反;
如果是一个合数,那么它显然是由两个质数相乘得到的,符号不变。
时间复杂度(O((r-l)log(r-l)))(不愧是小清新数学题)


代码

#include <cstdio>
#include <cmath>
#define rr register
using namespace std;
typedef long long lll;
const int P[5]={2,3,7,61,24251},N=1000011;
int prime[N],v[N],mu[N],cnt,ans; lll f[N],l,r;
inline lll mul(lll a,lll b,lll mod){return (a*b-(lll)((long double)a/mod*b)*mod+mod)%mod;}
inline lll ksm(lll x,lll y,lll mod){
	rr lll ans=1;
	for (;y;y>>=1,x=mul(x,x,mod))
	    if (y&1) ans=mul(ans,x,mod);
	return ans;
}
inline bool mr(lll n){
	if(n==46856248255981ll||n<2) return 0;
    if(n==2||n==3||n==7||n==61||n==24251) return 1;
    if (!(n&1)||!(n%3)||!(n%7)||!(n%61)||!(n%24251)) return 0;
    rr lll m=n-1; rr int Cnt=0;
    while (!(m&1)) m>>=1,++Cnt;
	for (rr int i=0;i<5&&P[i]<n;++i){
		rr lll now=ksm(P[i],m,n),last=now;
		for (rr int j=1;j<=Cnt;++j){
			now=mul(now,now,n);
			if (now==1&&last!=1&&last!=n-1) return 0;
			last=now;
		}
		if (now!=1) return 0;
	} 
	return 1;
}
signed main(){
	scanf("%lld%lld",&l,&r);
	for (rr int i=2;i<N;++i){
		if (!v[i]) prime[++cnt]=i;
		for (rr int j=1;j<=cnt&&prime[j]*i<N;++j){
			v[i*prime[j]]=1;
			if (i%prime[j]==0) break;
		}
	}
	for (rr lll i=l;i<=r;++i) mu[i-l]=1,f[i-l]=i;
	for (rr int i=1;i<=cnt;++i){
		rr int I=prime[i];
	    for (rr lll j=((l-1)/I+1)*I;j<=r;j+=I){
	    	rr int CNT=0;
	    	while (f[j-l]%I==0) ++CNT,f[j-l]/=I;
	    	if (CNT>1) mu[j-l]=0; else mu[j-l]*=-1;//指数大于1莫比乌斯函数为0,否则取反
		}
	}
	for (rr lll i=l;i<=r;++i)
	if (f[i-l]>1){
		if (floor(sqrt(f[i-l]))==sqrt(f[i-l])) mu[i-l]=0;//完全平方数为0
		    else if (mr(f[i-l])) mu[i-l]*=-1;//质数符号取反
		ans+=mu[i-l];
	}else ans+=mu[i-l];
	return !printf("%d",ans);
}
原文地址:https://www.cnblogs.com/Spare-No-Effort/p/13821724.html