1. 程式人生 > >BZOJ3994:約數個數和(莫比烏斯反演:求[1,N]*[1,M]的矩陣的因子個數)

BZOJ3994:約數個數和(莫比烏斯反演:求[1,N]*[1,M]的矩陣的因子個數)

Description

 設d(x)為x的約數個數,給定N、M,求  

Input

輸入檔案包含多組測試資料。

第一行,一個整數T,表示測試資料的組數。 接下來的T行,每行兩個整數N、M。

Output

 T行,每行一個整數,表示你所求的答案。

Sample Input

2
7 4
5 6

Sample Output

110
121

HINT

 1<=N, M<=50000


1<=T<=50000

思路:關鍵在於要知道X*Y的因子,為X的因子i和Y因子j的且滿足i和j互質的個數。

然後就可以莫比烏斯了。求gcd為1的個數,我們就加一個莫比烏斯係數進去....一系列操作後把常數提前,然後利用杜教篩分塊求出答案。

這裡有部分是求因子個數,因為這兩天感受到了線性篩的強大,所以我直接在篩莫比烏斯的時候篩出來了。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=50010;
int f[maxn],vis[maxn],p[maxn],num[maxn],low[maxn],cnt,mu[maxn];
void presolve()
{
    f[
1]=1; mu[1]=1; for(int i=2;i<maxn;i++){ if(!vis[i]) f[i]=2,p[++cnt]=i,mu[i]=-1,low[i]=i,num[i]=1; for(int j=1;j<=cnt&&p[j]*i<maxn;j++){ vis[i*p[j]]=1; if(i%p[j]==0){ mu[i*p[j]]=0; num[i*p[j]]=num[i]+1; low[i
*p[j]]=low[i]*p[j]; f[i*p[j]]=f[i]/(1+num[i])*(2+num[i]); break; } mu[i*p[j]]=-mu[i];low[i*p[j]]=p[j]; num[i*p[j]]=1; f[i*p[j]]=f[i]*2; } } for(int i=1;i<maxn;i++) f[i]+=f[i-1],mu[i]+=mu[i-1]; } int main() { presolve(); int T,N,M; ll ans; scanf("%d",&T); while(T--){ scanf("%d%d",&N,&M); ans=0; if(N>M) swap(N,M); for(int i=1,j;i<=N;i=j+1){ j=min(N/(N/i),M/(M/i)); ans+=(ll)(mu[j]-mu[i-1])*f[N/i]*f[M/i]; } printf("%lld\n",ans); } return 0; }