HDU-6390 GuGuFishtion(莫比烏斯反演)
阿新 • • 發佈:2019-02-18
題目: 給出n,m,p求
思路:phi(a)=a*(1-1/p1)*(1-1/p2)*.....*(1-1/pn) pi是a的素因子;
phi(a*b)=a*b*(1-1/p1)*(1-1/p2)*.....*(1-1/pm)裡面包含了a和b的所有素因子(去重後的)。
phi(a*b) / (phi(a)*phi(b)) 約分後的結果就是1/[ (1-1/p1)*(1-1/p2)*...*(1-1/pv) ] 其中p1,p2...pv是a和b的gcd
然後這個題就是求從1-n選一個x和1-m選一個y使得gcd(x,y)=k的組數,列舉每一個k,每次跑min(n,m)/k次,總複雜度是O(nlogn)。
前面要預處理以每個數作為gcd時的貢獻。 (第一次比賽時做出來莫比烏斯反演的題目...之前都是gg的,好菜。。)
#include <bits/stdc++.h> using namespace std; const int MAXN = 1000000; bool check[MAXN+10]; int prime[MAXN+10]; int mu[MAXN+10]; void Mobius() { memset(check,false,sizeof(check)); mu[1] = 1; int tot = 0; for(int i = 2; i <= MAXN; i++) { if( !check[i] ) { prime[tot++] = i; mu[i] = -1; } for(int j = 0; j < tot; j++) { if(i * prime[j] > MAXN) break; check[i * prime[j]] = true; if( i % prime[j] == 0) { mu[i * prime[j]] = 0; break; } else { mu[i * prime[j]] = -mu[i]; } } } } #define ll long long int b,d,t; ll p; ll inv[MAXN+10]; ll a[MAXN+10]; bool visit[MAXN+10]; void init(int N) { int i,j,n=N; for(int i=0;i<=N;i++) visit[i]=true; for(int i=0;i<=N;i++) a[i]=1; for (i=2;i<=n;i++) { ll tmp=1ll*i*inv[i-1]%p; if (visit[i]) { a[i]=a[i]*tmp%p; for (j=i+i;j<=n;j+=i) { a[j]=a[j]*tmp%p; visit[j]=false; } } } } int main() { Mobius(); scanf("%d",&t); while(t--) { scanf("%d%d%I64d",&b,&d,&p); inv[0]=inv[1]=1; for(int i=2;i<=b;i++) inv[i]=1ll*(p-p/i)*inv[p%i]%p; init(b); if(b>d) swap(b,d); ll ans=0; for(int k=1;k<=b;k++) { int bb=b/k,dd=d/k; ll num=0; for(int i=1;i<=bb;i++) num+=(ll)mu[i]*(bb/i)*(dd/i); num%=p; ans+=num*a[k]%p; if(ans>p) ans-=p; } printf("%I64d\n",ans); } return 0; }