【洛谷3653】小清新数学题(数论)
- 求\(\sum_{i=L}^R\mu(i)\)。
- \(1\le l\le r\le10^{18},r-l\le10^5\)
一道有趣的数论题
考虑\(l,r\)范围很大,我们最多只能筛出\(10^6\),也就是\(O(\sqrt[3]r)\)范围内的素数。
对于一个需要求解的\(\mu(x)\),我们可以先把它当中\(\le10^6\)的质因子先全部除去,同时维护出这部分的\(\mu\)值。
而剩下的部分如果不为\(1\),则最多由两个\(>10^6\)的质因子相乘而得。
如果剩下的是一个质数(可以用\(MillerRabin\)检测),那么答案就是在原\(\mu\)值的基础上再乘个\(-1\)。
如果剩下的是一个平方数,那么答案就是\(0\)。
否则,剩下的是两个不同的大质数,那么答案就是在原\(\mu\)值的基础上乘两次\(-1\),也就是原答案。
前面除质因子的过程最好不要枚举数去找质因子,而是枚举质因子直接扫过区间中的数,这样就能避免无效枚举。
代码:\(O((r-l)log^2r)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define S 1000000
#define LL long long
using namespace std;
int Mu[N+5];LL v[N+5];
int Pt,P[S+5];I void Sieve() {for(RI i=2,j;i<=S;++i)
for(!P[i]&&(P[++Pt]=i),j=1;j<=Pt&&i*P[j]<=S;++j) if(P[i*P[j]]=1,!(i%P[j])) break;}
namespace MR//MillerRabin素数测试
{
const int pt=4,p[pt]={2,3,13,19};
I LL QM(Con LL& x,Con LL& y,Con LL& X) {return (x*y-(LL)(1.0L*x*y/X)*X+X)%X;}//快速乘
I LL QP(LL x,LL y,Con LL& X) {LL t=1;W(y) y&1&&(t=QM(t,x,X)),x=QM(x,x,X),y>>=1;return t;}//快速幂
I bool T(Con LL& x,CI p) {if(!(x%p)||QP(p,x-1,x)^1) return 0;//如果是倍数或不符合费马测试
LL k=x-1,t;W(!(k&1)) if((t=QP(p,k>>=1,x))^1) return t==x-1;return 1;}//二次探测直至不为1
I bool IsP(Con LL& x) {for(RI i=0;i^pt;++i) {if(x==p[i]) return 1;if(!T(x,p[i])) return 0;}return 1;}//枚举质数检测
}
int main()
{
LL i,j,l,r;RI t=0;for(Sieve(),scanf("%lld%lld",&l,&r),i=l;i<=r;++i) v[i-l+1]=i,Mu[i-l+1]=1;//初始化每个数值为自身,μ为1
for(j=1;j<=Pt;++j) for(i=((l-1)/P[j]+1)*P[j]-l+1;i<=r-l+1;i+=P[j]) Mu[i]*=-1,!((v[i]/=P[j])%P[j])&&(Mu[i]=0);//枚举质因子去区间内更新
#define IsSqr(x) ((LL)sqrt(x)*(LL)sqrt(x)==(x))//判断平方数
for(i=1;i<=r-l+1;t+=Mu[i++]) Mu[i]&&v[i]^1&&(MR::IsP(v[i])?Mu[i]*=-1:IsSqr(v[i])&&(Mu[i]=0));//对剩余数判断是质数/平方数/两个大质数相乘
return printf("%d\n",t),0;
}
待到再迷茫时回头望,所有脚印会发出光芒