[HDU6868]Absolute Math

本文深入探讨了一道数论与组合优化领域的竞赛题目,解析了如何通过质因子分解和积性函数的性质来简化问题,最终利用线性筛法高效求解。文章详细阐述了从题目理解到算法设计的全过程,包括对μ函数和积性函数的理解、g函数的定义及其实现,以及如何处理大规模数据输入和输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

f(n)=∑d∣n∣μ(d)∣\displaystyle f(n)=\sum_{d\mid n}|\mu(d)|f(n)=dnμ(d)
∑i=1mf(ni)mod  109+7\displaystyle\sum_{i=1}^mf(ni)\mod 10^9+7i=1mf(ni)mod109+7
1≤n,m≤1071\le n,m\le10^71n,m107


题解

w(n)w(n)w(n)表示nnn的质因子个数,根据μ\muμ的定义,有

f(n)=∑k=0w(n)(w(n)k)∣(−1)k∣=2w(n) \displaystyle f(n)=\sum_{k=0}^{w(n)}{w(n)\choose k}|(-1)^k|=2^{w(n)} f(n)=k=0w(n)(kw(n))(1)k=2w(n)

显然f(n)f(n)f(n)是一个积性函数,且满足f(ab)=f(a)f(b)f(gcd⁡(a,b))\displaystyle f(ab)=\frac{f(a)f(b)}{f(\gcd(a,b))}f(ab)=f(gcd(a,b))f(a)f(b)

化简原式:
∑i=1mf(in)=f(n)∑i=1mf(i)f(gcd⁡(n,i))=f(n)∑d∣n1f(d)∑i=1mf(i)[gcd⁡(i,n)=d]=f(n)∑d∣n1f(d)∑i=1⌊md⌋f(id)[gcd⁡(i,nd)=1]=f(n)∑d∣n1f(d)∑i=1⌊md⌋f(id)∑p∣i,p∣ndμ(p)=f(n)∑d∣n∑pd∣nμ(p)f(d)∑i=1⌊mpd⌋f(ipd)=f(n)∑T∣n∑d∣Tμ(Td)f(d)∑i=1⌊mT⌋f(iT)=f(n)∑T∣ng(T)∑i=1⌊mT⌋f(iT) \begin{aligned} \sum_{i=1}^mf(in)=&f(n)\sum_{i=1}^m\frac{f(i)}{f(\gcd(n,i))}\\ &=f(n)\sum_{d\mid n}\frac1{f(d)}\sum_{i=1}^mf(i)[\gcd(i,n)=d]\\ &=f(n)\sum_{d\mid n}\frac1{f(d)}\sum_{i=1}^{\lfloor\frac md\rfloor}f(id)[\gcd(i,\frac nd)=1]\\ &=f(n)\sum_{d\mid n}\frac1{f(d)}\sum_{i=1}^{\lfloor\frac md\rfloor}f(id)\sum_{p|i,p|\frac nd}\mu(p)\\ &=f(n)\sum_{d\mid n}\sum_{pd\mid n}\frac{\mu(p)}{f(d)}\sum_{i=1}^{\lfloor\frac m{pd}\rfloor}f(ipd)\\ &=f(n)\sum_{T\mid n}\sum_{d\mid T}\frac{\mu(\frac Td)}{f(d)}\sum_{i=1}^{\lfloor\frac mT\rfloor}f(iT)\\ &=f(n)\sum_{T\mid n}g(T)\sum_{i=1}^{\lfloor\frac mT\rfloor}f(iT) \end{aligned} i=1mf(in)=f(n)i=1mf(gcd(n,i))f(i)=f(n)dnf(d)1i=1mf(i)[gcd(i,n)=d]=f(n)dnf(d)1i=1dmf(id)[gcd(i,dn)=1]=f(n)dnf(d)1i=1dmf(id)pi,pdnμ(p)=f(n)dnpdnf(d)μ(p)i=1pdmf(ipd)=f(n)TndTf(d)μ(dT)i=1Tmf(iT)=f(n)Tng(T)i=1Tmf(iT)

其中g(T)=∑d∣Tμ(Td)f(d)\displaystyle g(T)=\sum_{d\mid T}\frac{\mu(\frac Td)}{f(d)}g(T)=dTf(d)μ(dT),即g=1f×μ\displaystyle g=\frac1f\times \mug=f1×μ,也是一个积性函数,用线性筛的方法推导可得:

g(T)={(−12)w(T),T=∏i=1w(T)pi0,otherwise g(T)=\left\{\begin{aligned} \Big(-\frac12\Big)^{w(T)},&&T=\prod_{i=1}^{w(T)} p_i\\ 0,&&{\rm otherwise} \end{aligned}\right. g(T)=(21)w(T),0,T=i=1w(T)piotherwise

也就是说对于T∣nT\mid nTng(T)g(T)g(T),只有当TTTnnn的质因子集合的某个子集中所有元素的乘积时,才会产生贡献。而n≤107n\le10^7n107时,w(n)≤8w(n)\le8w(n)8,故可以在O(8⋅28)O(8\cdot2^8)O(828)的时间内枚举出所有有贡献的TTT,这样比直接枚举约数要快一些。

由于内存限制,∑i=1⌊mT⌋f(iT)\displaystyle \sum_{i=1}^{\lfloor\frac mT\rfloor}f(iT)i=1Tmf(iT)不能预处理。

考虑离线,把所有∑i=1⌊mT⌋f(iT)\displaystyle \sum_{i=1}^{\lfloor\frac mT\rfloor}f(iT)i=1Tmf(iT)的询问,按TTT为第一关键字,⌊mT⌋\lfloor\frac mT\rfloorTm为第二关键字排序然后暴力计算即可。

计算答案时注意要乘上f(n)g(T)f(n)g(T)f(n)g(T)的系数。

时间复杂度O(nlog⁡n+T(n+8⋅28))O(n\log n+T(\sqrt n+8\cdot 2^8))O(nlogn+T(n+828))

#include <bits/stdc++.h>
using namespace std;
const int N = 1e7 + 5, P = 1e9 + 7, Inv2 = (P + 1) / 2;
typedef long long ll;
struct Data {
    int d, m, w, id;
};
int n, m, q, Ans[10005];
int is[N], prime[N], f[N], g[N];
vector<Data> Q;
int main() {
    n = 1e7;
    f[1] = g[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (!is[i])
            prime[++prime[0]] = i, f[i] = 2, g[i] = P - Inv2;
        for (int j = 1, x; j <= prime[0] && (x = i * prime[j]) <= n; ++j) {
            is[x] = 1;
            if (i % prime[j])
                f[x] = 2 * f[i] % P, g[x] = (ll)g[prime[j]] * g[i] % P;
            else {
                f[x] = f[i];
                break;
            }
        }
    }
    scanf("%d", &q);
    for (int T = 1; T <= q; ++T) {
        scanf("%d%d", &n, &m);
        int t = n;
        vector<int> div;
        for (int d = 2; d * d <= t; ++d)
            if (t % d == 0) {
                div.push_back(d);
                while (t % d == 0)
                    t /= d;
            }
        if (t != 1)
            div.push_back(t);
        for (int s = 0; s < (1 << div.size()); ++s) {
            int d = 1;
            for (int i = 0; i <= (int)div.size() - 1; ++i)
                if (s & (1 << i))
                    d *= div[i];
            if (m < d || !g[d])
                continue;
            Q.push_back({d, (m / d) * d, int((ll)f[n] * g[d] % P), T});
        }
    }
    sort(Q.begin(), Q.end(),
         [](Data a, Data b) { return a.d == b.d ? a.m < b.m : a.d < b.d; });
    ll w;
    int d = 0, i;
    for (auto t : Q) {
        if (t.d != d)
            d = i = t.d, w = 0;
        for (; i <= t.m; i += d)
            w += f[i];
        Ans[t.id] = (Ans[t.id] + (ll)t.w * (w %= P) % P) % P;
    }
    for (int i = 1; i <= q; ++i)
        printf("%d\n", Ans[i]);
    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值