Sdoi2015约数个数和题解莫比乌斯反演

题目描述
  • T组数据,求 ΣNi=1ΣMj=1d(ij) d(x) 代表x的约数个数。

  • 1N,M,T105

题解
  • 首先,膜拜一下PoPoQQQ大神及其题解

  • 然后,有一个神奇的结论: ΣNi=1ΣMj=1d(ij) = ΣNi=1ΣMj=1[Ni][Mj][gcd(i,j)=1]
    要证上式,只需证 d(nm)=Σi|nΣj|m[gcd(i,j)=1] ,因为上式即为该式的前缀和形式。
    分开考虑每个质数 p 对答案的贡献。设n=npk1,m=mpk2,那 p d(nm)的贡献就是 k1+k2+1 ,对等式右边的贡献是有序数对 (i,j) 中的 (pk1,1)(pk11,1)(pk12,1)(1,1)(1,p)(1,pk21)(1,pk2) 也是 k1+k2+1 ,所以原式得证。

  • 再令 f(x)=Σxi=1[xi] ,显然有 f(x)=d(x) ,因为 f(x) 相当于把 i=1x d(x) 的贡献求和,就是 d(x) 本身

  • 然后可以莫比乌斯反演了:

    ans=ΣNi=1ΣMj=1[Ni][Mj][gcd(i,j)=1]=ΣNi=1ΣMj=1Σk|gcd(i,j)μ(k)[Ni][Mj]=ΣNk=1Σ[Nk]i=1Σ[Mk]j=1μ(k)[Nki][Mkj]=ΣNk=1μ(k)Σ[Nk]i=1[Nki]Σ[Mk]j=1[Nkj]=ΣNk=1μ(k)f([Mk])f([Mk])=ΣNk=1μ(k)d([Nk])d([Mk])

  • 然后用筛法筛出 μ(i) d(i) 再做就好了。

  • 看来还需要锻炼自己的数学功底啊

Code
#include <cstdio>
#include <algorithm>
#define ll long long
#define M 50000
using namespace std;
int mu[M + 5], n, m, pri[M], top, d[M + 5], f[M + 5];
bool mrk[M + 5];
void getmu()
{
    mu[1] = d[1] = 1;
    for(int i = 2; i <= M; ++i)
    {
        if(!mrk[i])
        {
            pri[top++] = i;
            mu[i] = -1;
            d[i] = 2;
            f[i] = 1;
        }
        for(int j = 0; j < top && i * pri[j] <= M; ++j)
        {
            mrk[i * pri[j]] = true;
            if(i % pri[j] == 0)
            {
                mu[i * pri[j]] = 0;
                f[i * pri[j]] = f[i] + 1;
                d[i * pri[j]] = d[i] / (f[i] + 1) * (f[i] + 2);
                break;
            }
            mu[i * pri[j]] = -mu[i];
            d[i * pri[j]] = (d[i] << 1);
            f[i * pri[j]] = 1;
        }
    }
    for(int i = 2; i <= M; ++ i)
    {
        d[i] += d[i - 1];
        mu[i] += mu[i - 1];
    }
}
inline ll work(int n, int m)
{
    if(n > m) swap(n, m);
    ll ans = 0;
    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]) * (ll)d[n / i] * (ll)d[m / i];
    }
    return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("d.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif
    int T;
    getmu();
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &m);
        printf("%lld\n", work(n, m));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值