luogu 4844 LJJ爱数数 (莫比乌斯反演+数学推导)
题目大意:求满足gcd(a,b,c)==1,1/a+1/b=1/c,a,b,c<=n的{a,b,c}有序三元组个数
因为题目里有LJJ我才做的这道题
出题人官方题解https://www.cnblogs.com/Blog-of-Eden/p/9367521.html对我帮助很大
思维很巧妙的一道题,佩服出题人Orzzz
由原式可得,$c=\frac{ab}{a+b}$
令g=gcd(a,b),A=a/g,B=b/g,显然gcd(g,c)==1,gcd(A,B)==1
带入可得$\frac{ABg^{2}}{(A+B)g}=c$ <=> $\frac{ABg}{A+B}=c$
因为A,B互质,所以$A,B,A+B$两两互质
由$\frac{ABg}{A+B}=c$可得
因为c是整数,A与A+B互质,B与$A+B互质,所以当且仅当(A+B)|g
令G=g/(A+B),可得ABG=c,所以G|c,而g与c互质,所以G作为g的因子,与c也一定互质,即gcd(G,c)==1,所以G只能等于1
综上,可得g=A+B,c=AB,a+b=$g^{2}$
现在大问题转化成了一般性问题,每次枚举一个g,求在一定范围内,g=A+B且gcd(A,B)==1的数对数量
显然这样的数对数量可以用莫比乌斯反演求得
由于g<=$\sqrt(2n)$,g<$2*10^{6}$,通过预处理,分解质因数是时间可以优化成logn,再筛出它所有的因子,利用莫比乌斯函数的容斥性质,即可求得合法数对数量
筛出每个数的因子的时间是均摊logn(调和级数),但由于空间限制,不能预处理出每个数的所有因子..
而A的合法范围则是保证A<=g,A*g<=n&&B*g<=n
细节需要多思考
1 #include <cmath> 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #define N 2001000 7 #define maxn 2000000 8 #define ll long long 9 #define uint unsigned int 10 using namespace std; 11 12 ll n; 13 int cnt; 14 15 int pr[N],use[N],mu[N],pmu[N],nxt[N]; 16 void get_pr() 17 { 18 mu[1]=pmu[1]=1; 19 for(int i=2;i<=maxn;i++) 20 { 21 if(!use[i]) pr[++cnt]=i,nxt[i]=i,mu[i]=-1; 22 pmu[i]=pmu[i-1]+mu[i]; 23 for(int j=1;j<=cnt&&i*pr[j]<=maxn;j++){ 24 use[i*pr[j]]=1,nxt[i*pr[j]]=pr[j]; 25 if(i%pr[j]==0){ 26 mu[i*pr[j]]=0; 27 break; 28 }else{ 29 mu[i*pr[j]]=-mu[i]; 30 } 31 } 32 } 33 } 34 int son[N],d[N],ps[N],num,nson; 35 void dfs_son(int s,int dep) 36 { 37 if(dep>num) {son[++nson]=s;return;} 38 for(int j=0;j<=d[dep];j++) 39 dfs_son(s,dep+1),s*=ps[dep]; 40 } 41 void get_son(int x) 42 { 43 num=0; 44 while(x!=1){ 45 int p=nxt[x];ps[++num]=p;d[num]=0; 46 while(x%p==0) x/=p,d[num]++; 47 } 48 if(x!=1) ps[++num]=x,d[num]=1; 49 for(int i=1;i<=nson;i++) son[i]=0; 50 nson=0; 51 dfs_son(1,1); 52 } 53 ll solve(int l,int r,int g) 54 { 55 ll ans=0;l--; 56 get_son(g); 57 for(int i=1;i<=nson;i++) 58 ans+=1ll*mu[son[i]]*(r/son[i]-l/son[i]); 59 return ans; 60 } 61 62 int main() 63 { 64 //freopen("t1.in","r",stdin); 65 scanf("%lld",&n); 66 ll sq=sqrt(n*2); 67 get_pr(); 68 ll ans=0; 69 for(ll g=1;g<=sq;g++) 70 { 71 int l=max(1ll,((g*g-n)/g+( ((g*g-n)%g==0)?0:1 ))); 72 int r=min(g-1,n/g); 73 if(l>r) continue; 74 ans+=solve(l,r,g); 75 } 76 printf("%lld\n",ans); 77 return 0; 78 }