1. 程式人生 > >luogu 4844 LJJ愛數數 (莫比烏斯反演+數學推導)

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 }