P3327 约数的个数和 [约数函数性质,数论分块]

P3327 约数的个数和

题意

d ( x ) d(x) d(x)为约数的个数,对于每个询问,回答 ∑ i = 1 n ∑ j = 1 m d ( i j ) \sum_{i=1}^n\sum_{j=1}^md(ij) i=1nj=1md(ij).

题解

这个题推得我头皮发麻,然后还没推出来,后来发现要做这题的先知道一个性质:

d ( i j ) = ∑ x ∣ i ∑ y ∣ j [ g c d ( x , y ) = 1 ] d(ij)=\sum_{x|i}\sum_{y|j}[gcd(x,y)=1] d(ij)=xiyj[gcd(x,y)=1]

通过这个性质,我们把原式写成
∑ i = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j [ g c d ( x , y ) = 1 ] \sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[gcd(x,y)=1] i=1nj=1mxiyj[gcd(x,y)=1]

我们知道 ∑ d ∣ x μ ( d ) = [ x = 1 ] \sum_{d|x}\mu(d)=[x=1] dxμ(d)=[x=1],代换进去,就得到了:

∑ i = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j ∑ d ∣ g c d ( x , y ) μ ( d ) \sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}\sum_{d|gcd(x,y)}\mu(d) i=1nj=1mxiyjdgcd(x,y)μ(d)

变枚举 i , j i,j i,j为枚举 x , y x,y x,y:

∑ x = 1 n ∑ y = 1 m ⌊ n x ⌋ ⌊ m y ⌋ ∑ d ∣ g c d ( x , y ) μ ( d ) \sum_{x=1}^n\sum_{y=1}^m \lfloor \frac{n}{x} \rfloor \lfloor \frac{m}{y} \rfloor\sum_{d|gcd(x,y)}\mu(d) x=1ny=1mxnymdgcd(x,y)μ(d)

再转为枚举 d d d,得到:

∑ d = 1 μ ( d ) ∑ x = 1 n / d ∑ y = 1 m / d ⌊ n x d ⌋ ⌊ m y d ⌋ \sum_{d=1}\mu(d)\sum_{x=1}^{n/d}\sum_{y=1}^{m/d} \lfloor \frac{n}{xd} \rfloor \lfloor \frac{m}{yd} \rfloor d=1μ(d)x=1n/dy=1m/dxdnydm

也即

∑ d = 1 μ ( d ) ( ∑ x = 1 n / d ⌊ n x d ⌋ ) ( ∑ y = 1 m / d ⌊ m y d ⌋ ) \sum_{d=1}\mu(d)(\sum_{x=1}^{n/d} \lfloor \frac{n}{xd} \rfloor) (\sum_{y=1}^{m/d} \lfloor \frac{m}{yd} \rfloor) d=1μ(d)(x=1n/dxdn)(y=1m/dydm)

f ( x ) = ∑ i = 1 x ⌊ x i ⌋ f(x)=\sum_{i=1}^x \lfloor \frac{x}{i} \rfloor f(x)=i=1xix,则原式:

∑ d = 1 μ ( d ) f ( ⌊ n d ⌋ ) f ( ⌊ m d ⌋ ) \sum_{d=1}\mu(d)f(\lfloor \frac{n}{d} \rfloor)f(\lfloor \frac{m}{d} \rfloor) d=1μ(d)f(dn)f(dm)

f ( x ) f(x) f(x)可以 O ( 1 ) O(1) O(1)查询的话,上面的式子就可以 O ( n ) O(\sqrt{n}) O(n )数论分块求出.

显然, f ( x ) f(x) f(x)可以用 O ( n n ) O(n\sqrt{n}) O(nn )的时间复杂度预处理出来,方法也是数论分块.

代码

// luogu-judger-enable-o2
#include <iostream>
#include <algorithm>
#include <cstring>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
typedef long long LL;
const int N = 50010;
int n,m,T;
int prime[N+10],mu[N+10],pcnt,zhi[N+10],low[N+10];
void sieve() {
    mu[1] = zhi[1] = 1;
    for(int i = 2;i <= N;++i) {
        if(!zhi[i]) {
            prime[pcnt++] = i;
            mu[i] = -1;
        }
        for(int j = 0;j < pcnt && i * prime[j] <= N;++j) {
            zhi[i*prime[j]] = 1;
            if(i % prime[j] == 0) {
                mu[i*prime[j]] = 0;
                break;
            }
            else{
                mu[i*prime[j]] = -mu[i];
            }
        }
    }
}
LL F[N+10];
int main() {
    std::ios::sync_with_stdio(false);
    std::cin >> T;
    sieve();
    for(int i = 1;i <= N;++i) {
        mu[i] += mu[i-1];
    }
    for(int i = 1;i <= N;++i) {
        for(int x = 1,last;x <= i;x = last+1) {
            last = i/(i/x);
            F[i] += (last-x+1)*(i/x);
        }
    }
    while(T--) {
        std::cin >> n >> m;
        LL ans = 0;
        int lim = n > m?m:n;
        for(int x = 1,nx1,nx2,nxt;x <= lim;x = nxt+1) {
            nx1 = n/(n/x);
            nx2 = m/(m/x);
            nxt = nx1>nx2?nx2:nx1;
            ans += (mu[nxt]-mu[x-1])*F[n/x]*F[m/x];
        }
        std::cout << ans << std::endl;
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值