CF915G Coprime Arrays 题解

题意

给定 \(n, k\),对于所有的 \(m \in \left[1, k\right]\),求长度为 \(n\),值域为 \(\left[1,m \right]\) 且最大公约数为 \(1\) 的序列种数,对 \(10^9 + 7\) 取模。

\(1 \le n,k \le 2 \times 10^6\))。

题解

\(f(d, m)\) 表示长度为 \(n\),值域为 \(\left[1,m \right]\) 且最大公约数为 \(1\) 的序列种数,\(g(d, m)\) 表示长度为 \(n\),值域为 \(\left[1,m \right]\) 且序列元素均为 \(d\) 的倍数即最大公约数为 \(d\) 的倍数的序列种数,那么有

\[\begin{aligned} g(d, m) &= \sum\limits_{d \mid h} f(h, m) \\ f(d, m) &= \sum\limits_{d \mid h} \mu\left(\dfrac{h}{d}\right) g(h, m) \end{aligned}\]

考虑计算 \(g(d, m)\),发现

\[g(d, m) = \left\lfloor\dfrac{m}{d}\right\rfloor^n \]

综上,可得

\[f(1, m) = \sum\limits_{i = 1}^{m} \mu\left(i\right)\left\lfloor\dfrac{m}{i}\right\rfloor^n \]

现在我们得到了答案的计算式,但是若对于每个 \(m\) 应用整除分块计算,复杂度为 \(\mathcal{O}(k \sqrt{k} \log n)\),若使用线性筛预处理出幂函数,那么复杂度为 \(\mathcal{O}(k \sqrt{k})\),均无法通过本题。

故考虑在 \(f(1, m - 1)\) 的基础上计算 \(f(1, m)\),考虑 \(f(1, m)\)\(f(1, m - 1)\) 的差值均为 \(\left\lfloor\dfrac{m}{i}\right\rfloor\) 的改变引起的,而对于每个 \(i\),只有 \(m\) 增大为 \(i\) 的倍数时该值才会改变,故答案共会改变 \(\mathcal{O}(k \ln k)\) 次。形式化的,有

\[\begin{aligned} f(1, m) &= f(1, m - 1) + \left(f(1, m) - f(1, m - 1)\right) \\ &= f(1, m - 1) + \left(\sum\limits_{i = 1}^{m} \mu\left(i\right)\left\lfloor\dfrac{m}{i}\right\rfloor^n - \sum\limits_{i = 1}^{m - 1} \mu\left(i\right)\left\lfloor\dfrac{m - 1}{i}\right\rfloor^n\right) \\ &= f(1, m - 1) + \sum\limits_{i = 1}^{m}\mu\left(i\right)\times\left(\left\lfloor\dfrac{m}{i}\right\rfloor^n - \left(\left\lfloor\dfrac{m}{i}\right\rfloor - 1\right)^n\right) \end{aligned}\]

\(\left\lfloor\dfrac{m}{i}\right\rfloor^n - \left(\left\lfloor\dfrac{m}{i}\right\rfloor - 1\right)^n\) 的取值只有 \(i \mid m\) 时才不为 \(0\),故上式后半部分在 \(m\) 取遍 \(\left[1, k\right]\) 时产生贡献的次数共为 \(\mathcal{O}(k \ln k)\) 次。

对于每个数,预处理其因子,在 \(m\) 增长后,对于当前 \(m\) 的所有因子 \(i\)\(\left\lfloor\dfrac{m}{i}\right\rfloor\) 的值均会增长 \(1\),将答案累加 \(\mu\left(i\right)\times\left(\left\lfloor\dfrac{m}{i}\right\rfloor^n - \left(\left\lfloor\dfrac{m}{i}\right\rfloor - 1\right)^n\right)\) 即可。

使用线性筛预处理出幂函数,在 \(n,k\) 同阶的情况下,总复杂度为 \(\mathcal{O}(n \log n)\),可以通过本题。

但是让我们再次审视差分函数 \(S(n)\),可以列出其表达式

\[S(n) = \sum\limits_{d \mid n} \mu\left(d\right) S\left(\dfrac{n}{d}\right) \]

可以若没有 \(\mu\) 函数的干扰,函数 \(S\) 可以通过 \(\tt{Dirichlet}\) 前缀和以 \(\mathcal{O}(n \log\log n)\) 的复杂度筛出,所以我们考虑 \(\mu\) 函数对普通 \(\tt{Dirichlet}\) 前缀和的影响。

考虑完全平方数的 \(\mu\) 值为 \(0\) 这一性质,可以发现对于每个质因子 \(p\),其最多贡献一次,不同于普通 \(\tt{Dirichlet}\) 前缀和的贡献无数次。故可以类比完全背包和 \(01\) 背包的区别,将普通 \(\tt{Dirichlet}\) 前缀和的第二个循环语句改为从大到小遍历,这样就可以保证每个质因子最多贡献一次。

普通 \(\tt{Dirichlet}\) 前缀和:

for (auto const &iter: P)
    for (valueType i = 1; i <= K / iter; ++i)
        Dec(F[i * iter], F[i]);

本次魔改 \(\tt{Dirichlet}\) 前缀和:

for (auto const &iter: P)
    for (valueType i = K / iter; i >= 1; --i)
        Dec(F[i * iter], F[i]);

其中 \(P\) 为质数集。

这样处理的话复杂度为 \(\mathcal{O}(n \log \log n)\),目前 \(\tt{Codeforces}\) 最优解。

img

Code

\(\mathcal{O}(n \log n)\)

#include <bits/stdc++.h>

typedef int valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<bool> bitset;

constexpr valueType MOD = 1e9 + 7;

template<typename T1, typename T2, typename T3 = valueType>
void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
    a = a + b;

    if (a >= mod)
        a -= mod;
}

template<typename T1, typename T2, typename T3 = valueType>
T1 sub(T1 a, T2 b, const T3 &mod = MOD) {
    return a - b < 0 ? a - b + mod : a - b;
}

template<typename T1, typename T2, typename T3 = valueType>
T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
    return (long long) a * b % mod;
}

template<typename T1, typename T2, typename T3 = valueType>
void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
    a = (long long) a * b % mod;
}

template<typename T1, typename T2, typename T3 = valueType>
T1 pow(T1 a, T2 b, const T3 &mod = MOD) {
    T1 result = 1;

    while (b > 0) {
        if (b & 1)
            Mul(result, a, mod);

        Mul(a, a, mod);
        b = b >> 1;
    }

    return result;
}

class LineSieve {
public:
    typedef std::vector<valueType> container;

private:
    valueType N, M;
    container prime;
    bitset isPrime;
    container Mobius, Power;
    ValueMatrix Factor;

public:
    LineSieve(valueType n, valueType m) : N(n), M(m), prime(), isPrime(n + 1, true), Mobius(n + 1, 1), Factor(n + 1), Power(n + 1) {
        Mobius[1] = 1;
        Power[0] = 0;
        Power[1] = 1;

        for (valueType i = 2; i <= N; ++i) {
            if (isPrime[i]) {
                prime.push_back(i);

                Mobius[i] = -1;

                Power[i] = ::pow(i, M);
            }

            for (auto const &iter: prime) {
                valueType const t = i * iter;

                if (t > N)
                    break;

                isPrime[t] = false;

                Power[t] = mul(Power[i], Power[iter]);

                if (i % iter == 0) {
                    Mobius[t] = 0;

                    break;
                } else {
                    Mobius[t] = -Mobius[i];
                }
            }

            if (Mobius[i] < 0)
                Mobius[i] += MOD;
        }

        for (valueType i = 1; i <= N; ++i)
            for (valueType j = i; j <= N; j += i)
                Factor[j].push_back(i);
    }

    valueType operator()(valueType n) const {
        return Mobius[n];
    }

    valueType pow(valueType n) const {
        return Power[n];
    }

    const ValueVector &fact(valueType n) const {
        return Factor[n];
    }
};

int main() {
    valueType N, K;

    std::cin >> N >> K;

    LineSieve const sieve(K, N);

    ValueVector ans(K + 1, 0);

    for (valueType i = 1; i <= K; ++i) {
        ans[i] = ans[i - 1];

        for (auto const &iter: sieve.fact(i))
            Inc(ans[i], mul(sieve(iter), sub(sieve.pow(i / iter), sieve.pow(i / iter - 1))));
    }

    valueType result = 0;

    for (valueType i = 1; i <= K; ++i)
        Inc(result, ans[i] ^ i);

    std::cout << result << std::endl;
}

\(\mathcal{O}(n \log \log n)\)

#include <bits/stdc++.h>

typedef int valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<bool> bitset;

constexpr valueType MOD = 1e9 + 7;

template<typename T1, typename T2, typename T3 = valueType>
void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
    a = a + b;

    if (a >= mod)
        a -= mod;
}

template<typename T1, typename T2, typename T3 = valueType>
void Dec(T1 &a, T2 b, const T3 &mod = MOD) {
    a = a - b;

    if (a < 0)
        a += mod;
}

template<typename T1, typename T2, typename T3 = valueType>
T1 sub(T1 a, T2 b, const T3 &mod = MOD) {
    return a - b < 0 ? a - b + mod : a - b;
}

template<typename T1, typename T2, typename T3 = valueType>
T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
    return (long long) a * b % mod;
}

template<typename T1, typename T2, typename T3 = valueType>
void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
    a = (long long) a * b % mod;
}

template<typename T1, typename T2, typename T3 = valueType>
T1 pow(T1 a, T2 b, const T3 &mod = MOD) {
    T1 result = 1;

    while (b > 0) {
        if (b & 1)
            Mul(result, a, mod);

        Mul(a, a, mod);
        b = b >> 1;
    }

    return result;
}

class LineSieve {
public:
    typedef std::vector<valueType> container;

private:
    valueType N, M;
    container prime;
    bitset isPrime;
    container Power;

public:
    LineSieve(valueType n, valueType m) : N(n), M(m), prime(), isPrime(n + 1, true), Power(n + 1) {
        prime.reserve(n / 10);
        Power[0] = 0;
        Power[1] = 1;

        for (valueType i = 2; i <= N; ++i) {
            if (isPrime[i]) {
                prime.push_back(i);

                Power[i] = ::pow(i, M);
            }

            for (auto const &iter: prime) {
                valueType const t = i * iter;

                if (t > N)
                    break;

                isPrime[t] = false;

                Power[t] = mul(Power[i], Power[iter]);

                if (i % iter == 0)
                    break;
            }
        }
    }

    valueType pow(valueType n) const {
        return Power[n];
    }

    const container &Prime() const {
        return prime;
    }
};

int main() {
    valueType N, K;

    std::cin >> N >> K;

    LineSieve const sieve(K, N);

    ValueVector const &P = sieve.Prime();

    ValueVector F(K + 1, 0);

    for (valueType i = 1; i <= K; ++i)
        F[i] = sub(sieve.pow(i), sieve.pow(i - 1));

    for (auto const &iter: P)
        for (valueType i = K / iter; i >= 1; --i)
            Dec(F[i * iter], F[i]);

    for (valueType i = 1; i <= K; ++i)
        Inc(F[i], F[i - 1]);

    valueType result = 0;

    for (valueType i = 1; i <= K; ++i)
        Inc(result, F[i] ^ i);

    std::cout << result << std::endl;
}
posted @ 2023-09-02 10:14  User-Unauthorized  阅读(12)  评论(0编辑  收藏  举报