[bzoj3994] [SDOI2015]约数个数和

Description

设d(x)为x的约数个数,给定N、M,求 \(\sum_{i=1}^N\sum_{j=1}^Md(ij)\)html

Input

输入文件包含多组测试数据。c++

第一行,一个整数T,表示测试数据的组数。git

接下来的T行,每行两个整数N、M。函数

Output

T行,每行一个整数,表示你所求的答案。测试

Sample Input

2
7 4
5 6

Sample Output

110
121

HINT

1<=N, M<=50000spa

1<=T<=50000code

solution

前置知识:莫比乌斯反演htm

首先对于\(d\)函数有一个结论:
\[ d(ij)=\sum_{d|i}\sum_{d|j}[gcd(i,j)=1] \]
我就是由于不知道这个结论推了半个小时无果QAQblog

证实大体以下:对于\(ij\)的每个质因数\(x\),在\(i\)里出现了\(a\)次,在\(j\)里出现了\(b\)次,则总共有\(a+b+1\)中状况,ip

而后进行转换,对于任意一个要选\(q\)个的状况:

  • \(q\leqslant a\),就在\(i\)里选出\(q\)个,\(j\)里不选。
  • 不然就在\(j\)里选出\(q-a\)个,这里的在\(j\)里选实质上是在\(i\)里选满了,而后再在\(j\)里选的,为了避免重复计数,在\(j\)里选\(z\)个实质上是选了\(z+a\)个。

对于每个质因数都这么考虑,而后只要保证\(i,j\)互质即为一种方案。

而后把这个玩意带到题目给的式子里去,大力推一下,可得:
\[ ans=\sum_{d^\prime=1}^{min(n,m)}\mu(d^\prime)\sum_{d_1=1}^{\lfloor\frac{n}{d^\prime}\rfloor}\lfloor\frac{n}{d_1d^\prime}\rfloor\sum_{d_2=1}^{\lfloor\frac{m}{d^\prime}\rfloor}\lfloor\frac{m}{d_2d^\prime}\rfloor \]
\(f\)为式子后面那一块,即:
\[ f(n)=\sum_{i=1}^{n}\lfloor\frac{n}{i}\rfloor \]
而后考虑下这个函数的性质:

对于枚举的\(i\)\(\lfloor\frac{n}{i}\rfloor\)实质上就是\(n\)之内能整除\(i\)的数的个数。

反过来想,对于每一个数,它的每个约数都对答案有1的贡献,因此\(f(n)\)等价于\(n\)之内的全部数的约数个数和。

而后线筛下约数个数和莫比乌斯函数,求下前缀和,整除分块下,就作完了。

时间复杂度\(O(n+q\sqrt{n})\)

#include<bits/stdc++.h>
using namespace std;

#define int long long 

void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int maxn = 5e4+1;

int d[maxn],pri[maxn],tot,p[maxn],vis[maxn],mu[maxn];

void sieve() {
    d[1]=mu[1]=1;
    for(int i=2;i<maxn;i++) {
        if(!vis[i]) pri[++tot]=i,d[i]=2,p[i]=2,mu[i]=-1;
        for(int t,j=1;j<=tot&&i*pri[j]<maxn;j++) {
            vis[t=i*pri[j]]=1;
            if(!(i%pri[j])) {
                p[t]=p[i]+1;d[t]=d[i]/p[i]*p[t];
                mu[t]=0;break;
            }
            p[t]=2,d[t]=d[i]*p[t];mu[t]=-mu[i];
        }
    }
    for(int i=1;i<maxn;i++) mu[i]=mu[i]+mu[i-1];
    for(int i=1;i<maxn;i++) d[i]=d[i-1]+d[i];
}

signed main() {
    sieve();int t;read(t);
    while(t--) {
        int n,m;read(n),read(m);
        int T=1,ans=0;if(n>m) swap(n,m);
        while(T<=n) {
            int pre=T;T=min(n/(n/T),m/(m/T));
            ans+=(mu[T]-mu[pre-1])*d[n/T]*d[m/T];T++;
        }write(ans);
    }
    return 0;
}
相关文章
相关标签/搜索