Advertisement

[51Nod 1584] 加权约数和(约数和函数性质 + 莫比乌斯反演) | 错题本

阅读量:

文章目录

  • 题目
  • 分析
  • 代码

题目

[51Nod 1584] 加权约数和

分析

先把 \max\{i, j\} 去掉:
原式=2\sum_{i = 1}^{N}\sum_{j = 1}^i i\sigma(ij)-\sum_{i = 1}^N i\sigma(i^2)


推式子前先介绍约数和函数有一个重要性质(和约数个数一样可以转化为 \gcd 问题):\begin{aligned}\sigma(xy)=&\sum_{i|x}\sum_{j|y}ij\left[\gcd\left(i, \frac{y}{j}\right)=1\right]\\ =&\sum_{i| x}\sum_{j|y}i\frac{y}{j}[\gcd(i, j) = 1]\end{aligned} 证明时考虑我们从 x 中枚举一个因数 i,从 y 中枚举一个因数 j,为了确保不算重,我们规定同一个质因子先从 y 中取到 j 里面,取完了再从 x 中取到 i 里面,这样的话所有 ij 都是 xy 的因数且不会算重。如何保证每个质因子都是先从 y 中取的呢?只需要看 \frac{y}{j} 中的某个质因数在不在 i 中,如果在,就意味着 y 中的这个质因数还没有取完,我们就从 x 中取了,这是不合法的,于是第一个等号得证,第二个等号就显然了。


首先计算第一个部分并将上述公式代入后得到以下结果:\begin{aligned}&\sum_{i = 1}^{N}\sum_{j = 1}^i i\sigma(ij)\\ =&\sum_{i = 1}^{N}\sum_{j = 1}^i i\sum_{x|i}\sum_{y|j}x\frac{j}{y}[\gcd(x, y) = 1]\end{aligned} 进行反演后上式变为:$$\begin{aligned}&\sum_{i = 1}^{N}\sum_{j = 1}^i i\sum_{x|i}\sum_{y|j}x\frac{j}{y}\sum_{d|\gcd(x, y)} \mu(d)\ =&\sum_{d = 1}^N\mu(d)\sum_{i = 1}^{\left\lfloor\frac{N}{d}\right\rfloor}\sum_{j = 1}^{i}di \cdot \frac{j}{y}\ =&\sum_{d = 1}^N\mu(d)\sum_{i = 1}^{\left\lfloor\frac{N}{d}\right\rfloor}\sum_{j = 1}^{i}\frac{di \cdot j}{y}\ =&~…(后续内容略)


对于任何满足形式

A(N)= \sum _{i=1}^{N} f(i)\cdot \sum _{j=1}^{\left \lfloor \dfrac {N}{i}\right \rfloor }g(j)

的表达式,在已知函数f,g的情况下都可以在时间复杂度上达到O(N log N),从而预处理所有的值序列$. 这一结论来源于其递推关系式:

A(N)= A(N-1)+ (f*g)(N)

可以看出当计算到第N项时,

A[N] = A[N-1] + \sum _{d|n} f(d)\cdot g(n/d)

即等于当前增量带来的额外变化量与之相关. 进而可以通过调和级数的方法来计算所有的卷积结果.


针对这道题的求解过程,在解决此问题时,首先进行μ和σ的线性筛选,在完成上述操作后进行相应的优化计算流程

代码

复制代码
    #include <bits/stdc++.h>
    
    int Read() {
    int x = 0; char c = getchar();
    while (c < '0' || c > '9')
        c = getchar();
    while (c >= '0' && c <= '9')
        x = x * 10 + (c ^ 48), c = getchar();
    return x;
    }
    
    typedef long long LL;
    
    const int MAXN = 1000000;
    const int MOD = 1000000007;
    
    inline int Add(int x, const int &y) {
    return ((x += y) >= MOD) ? (x - MOD) : x;
    }
    
    inline int Mul(const int &x, const int &y) {
    return (LL)x * y % MOD;
    }
    
    inline int Sub(int x, const int &y) {
    return ((x -= y) < 0) ? (x + MOD) : x;
    }
    
    int Mu[MAXN + 5];
    LL Sig[MAXN + 5], Low[MAXN + 5];
    bool Vis[MAXN + 5];
    std::vector<int> Pri;
    
    int Ans[MAXN + 5], S[MAXN + 5];
    
    void Init(int n) {
    Mu[1] = Sig[1] = Low[1] = S[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!Vis[i])
            Mu[i] = MOD - 1, Sig[i] = Low[i] = 1 + i, Pri.push_back(i);
        for (int j = 0, k; j < (int)Pri.size() && i * Pri[j] <= n; j++) {
            Vis[k = i * Pri[j]] = true;
            if (i % Pri[j] == 0) {
                Mu[k] = 0;
                Low[k] = 1 + Low[i] * Pri[j];
                Sig[k] = Sig[i] / Low[i] * Low[k];
                break;
            }
            Mu[k] = MOD - Mu[i];
            Low[k] = 1 + Pri[j];
            Sig[k] = Sig[i] * Low[k];
        }
        S[i] = Add(S[i - 1], Sig[i] % MOD);
    }
    for (int i = 1; i <= n; i++)
        for (int j = i; j <= n; j += i)
            Ans[j] = Add(Ans[j], Mul(Mu[i], Mul(i, Mul(i, Mul(j / i, Mul(Sig[j / i], Sub(Mul(2, S[j / i]), Sig[j / i])))))));
    for (int i = 1; i <= n; i++)
        Ans[i] = Add(Ans[i], Ans[i - 1]);
    }
    
    int main() {
    Init(MAXN);
    int T = Read(), Cas = 0;
    while (T--)
        printf("Case #%d: %d\n", ++Cas, Ans[Read()]);
    return 0;
    }
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解释

全部评论 (0)

还没有任何评论哟~