[SDOI2015][bzoj3994] 约数个数和 [莫比乌斯反演]

题面:

传送门

思路:

首先,我们需要证明一个结论:d(i*j)等于sigma(gcd(x,y)==1),其中x为i的约数,y为j的约数

对于nm的每一个质因子pi分别考虑,设n = pi^ai + n',m = pi^bi + m'

那么显然质因子pi对d(nm)的贡献为(ai+bi+1)

同理,考虑右边的式子,我们发现质数pi对右侧做的贡献仍然是(ai+bi+1),即如下的(x,y)

(pi^ai,1) (pi^(ai-1),1) ..... (1,1) .....(1,pi^(bi-1)) (1,pi^bi)

因此左右两式相同

因此原待求表达式化为如下形式:

由莫比乌斯函数第二情况得:上式可化为

其中g(i)表示前半个式子中的那段东西,相当于d(i)的前缀和

于是O(Tsqrt(min(n,m))轻松解决

顺便说一句,求约数个数也有线性的方法

记录c[i]表示i的最小的质因子的次数

每次更新这个,然后同时用c[i]+1更新d[i*pri[j]]即可

Code:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define ll long long
 6 using namespace std;
 7 inline ll read(){
 8     ll re=0,flag=1;char ch=getchar();
 9     while(ch>'9'||ch<'0'){
10         if(ch=='-') flag=-1;
11         ch=getchar();
12     }
13     while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
14     return re*flag;
15 }
16 ll mu[100010],pri[100010],c[100010],d[100010],cnt;bool vis[100010];
17 void init(ll n){
18     mu[1]=d[1]=c[1]=1;ll i,j,k;
19     for(i=2;i<=n;i++){
20         if(!vis[i]){
21             pri[++cnt]=i;mu[i]=-1;c[i]=1;d[i]=2;
22         }
23         for(j=1;(j<=cnt)&&(i*pri[j]<=n);j++){
24             k=i*pri[j];vis[k]=1;
25             if(i%pri[j]==0){
26                 d[k]=d[i]/(c[i]+1)*(c[i]+2);
27                 c[k]=c[i]+1;break;
28             }
29             mu[k]=-mu[i];
30             d[k]=d[i]*d[pri[j]];c[k]=1;
31         }
32     }
33     for(i=1;i<=n;i++) mu[i]+=mu[i-1];
34     for(i=1;i<=n;i++) d[i]+=d[i-1];
35 }
36 ll n,m;
37 int main(){
38     ll i,j,T=read(),ans;init(50000);
39     while(T--){
40         n=read();m=read();ans=0;
41         if(n>m) swap(m,n);
42         for(i=1;i<=n;i=j+1){
43             j=min(n/(n/i),m/(m/i));
44             ans+=(mu[j]-mu[i-1])*d[n/i]*d[m/i];
45         }
46         printf("%lld
",ans);
47     }
48 }
原文地址:https://www.cnblogs.com/dedicatus545/p/8492348.html