YY的gcd[莫比烏斯反演模板題]
阿新 • • 發佈:2018-12-15
YY的gcd啊,,題意如下;
多組資料
每組資料給出n,m,k;
求1<=i<=n,1<=j<=m,gcd(i,j)=k;的對數。
資料數最高為10000,n,m<=10000000
毒瘤資料。
根據上文所說,莫比烏斯反演最重要的第一步是找準f 和F兩個函式。
明顯本題f(d)指滿足gcd(i,j)的對數。
那F的含義就如上文所示了。
所以
把f(p)代入得
反演一下得
可以看出這裡我們在列舉質數,那我們換一下,列舉,並將F代入後半截得
為了方便表示,我們設T=dp
所以到現在為止,這個式子的複雜度已經是O(n)了。
但是由於是多組資料,所以我們要把複雜度降成O(根號下n);觀察式子發現很多向下取整得到的值是一樣的(在T不一樣的時候),所以我們運用整除分塊。整除分塊後一坨一坨處理就需要一個字首和sum。
整除分塊很簡單,,基本可以背程式碼,,大家請看程式碼就可以了。
#include<bits/stdc++.h> #define in read() using namespace std; int in{ int cnt=0,f=1;char ch=0; while(!isdigit(ch)){ ch=getchar(); if(ch=='-')f=-1; } while(isdigit(ch)){ cnt=cnt*10+ch-48; ch=getchar(); } return cnt*f; } int mul[10000003]; int prim[10000003],vis[10000003],cnt; long long g[10000003],sum[10000003];//g與sum見mull處理區,自行理解ovo int t; void mull(int n){ mul[1]=1; for(int i=2;i<=n;i++){ if(!vis[i]){ prim[++cnt]=i;mul[i]=-1; } for(int j=1;j<=cnt&&prim[j]*i<=n;j++){ vis[prim[j]*i]=1; if(i%prim[j]==0)break; mul[prim[j]*i]=-mul[i]; } }//篩完了mu for(int j=1;j<=cnt;j++){ for(int i=1;i*prim[j]<=n;i++){ g[prim[j]*i]+=mul[i];//g[i]存最終式子的最後面那一坨 } } for(int i=1;i<=n;i++)sum[i]=sum[i-1]+g[i];//g的字首和 } int main(){ t=in;int n,m; mull(10000000); while(t--){ n=in;m=in; if(n>m)swap(n,m); long long ans=0; for(register int l=1,r;l<=n;l=r+1){ r=min(n/(n/l),m/(m/l));//注意,這裡是整除分塊。經典的n/(n/l)保證分出來的複雜度最接近根號n ans+=1ll*(n/l)*(m/l)*(sum[r]-sum[l-1]);//字首和的運用。 } printf("%lld\n",ans); } return 0; }