cp-library-cpp

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub suisen-cp/cp-library-cpp

:heavy_check_mark: test/src/number/enumerate_quotient/divide_both.test.cpp

Depends on

Code

#define PROBLEM "https://atcoder.jp/contests/abc206/tasks/abc206_e"

#include <iostream>
#include <unordered_map>

#include "library/number/enumerate_quotient.hpp"

using namespace suisen;

constexpr int MAX = 1000010;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int l, r;
    std::cin >> l >> r;
    l = std::max(l - 1, 1);
    std::unordered_map<long long, long long> memo;
    auto f = [&memo](auto self, int l, int r) -> long long {
        const long long key = (long long) l * MAX + r;
        {
            auto it = memo.find(key);
            if (it != memo.end()) return it->second;
        }
        long long res = (long long) (r - l) * (r - l);
        for (auto [lg, rg, qs] : multiple_quotients_ranges(std::array<int, 2>{l, r})) {
            if (lg == 1 and rg == 1) continue;
            auto [nl, nr] = qs;
            res -= (rg - lg + 1) * self(self, nl, nr);
        }
        return memo[key] = res;
    };
    long long whole_pairs = (long long) (r - l) * (r - l);
    long long divisor_pairs = 0;
    for (auto [lg, rg, q] : quotient_ranges(r)) {
        divisor_pairs += (long long) std::max(0, (rg - std::max(lg - 1, l))) * q;
    }
    divisor_pairs += divisor_pairs - (r - l);
    std::cout << whole_pairs - f(f, l, r) - divisor_pairs << std::endl;
    return 0;
}
#line 1 "test/src/number/enumerate_quotient/divide_both.test.cpp"
#define PROBLEM "https://atcoder.jp/contests/abc206/tasks/abc206_e"

#include <iostream>
#include <unordered_map>

#line 1 "library/number/enumerate_quotient.hpp"



#include <algorithm>
#include <cassert>
#include <cmath>
#include <limits>
#include <tuple>
#include <vector>

namespace suisen {
    /**
     * @return #{ d>0 | exists an integer x s.t. floor(n/x)=d }
    */
    template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
    T quotient_num(const T& n) {
        const T h = sqrtl(n);
        return 2 * h - (h * (h + 1) > n);
    }

    /**
     * @brief Runs f(q) for each q>0 s.t. (there exists an integer x s.t. floor(n/x)=q) in ascending order.
     * @tparam T integer type
     */
    template <typename T, typename Fun,
        std::enable_if_t<std::conjunction_v<std::is_integral<T>, std::is_invocable<Fun, T>>, std::nullptr_t> = nullptr>
    void foreach_quotients(const T &n, Fun &&f) {
        assert(0 <= n);
        if (n == 0) return;
        const int h = sqrtl(n), m = 2 * h - (h == n / h);
        for (int l = 1; l <= h; ++l) f(l);
        if (n < 1LL << 53) { // the size of the fraction part of double is 52=53-1.
            for (int l = h - (m & 1); l >= 1; --l) f(static_cast<T>(static_cast<double>(n) / l));
        } else {
            for (int l = h - (m & 1); l >= 1; --l) f(n / l);
        }
    }
    /**
     * @brief Calculates an ascending vector of S = { d>0 | exists an integer x s.t. floor(n / x) = d } in O(√n) time.
     * @tparam T integer type
     * @return an ascending vector S = { d>0 | exists an integer x s.t. floor(n / x) = d }.
     */
    template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
    std::vector<T> enumerate_quotients(T n) {
        std::vector<T> q(quotient_num(n));
        auto it = q.begin();
        foreach_quotients(n, [&it](const T& v) { *it++ = v; });
        return q;
    }

    /**
     * @brief Calculates vector of { l = min S, r = max S, q }, where S = { d | floor(n / d) = q } in O(√n) time.
     * @tparam T integer type
     * @return vector of { l = min S, r = max S, q }, where S = { d | floor(n / d) = q }.
     * It is guaranteed that l, r is ascending and q is descending.
     */
    template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
    std::vector<std::tuple<T, T, T>> quotient_ranges(T n) {
        assert(0 <= n);
        const std::vector<T> qs = enumerate_quotients(n);
        const int m = qs.size();
        std::vector<std::tuple<T, T, T>> res(qs.size());
        for (int i = 0; i < m; ++i) {
            T l = i ? qs[i - 1] + 1 : 1, r = qs[i], q = qs[m - i - 1];
            res[i] = std::make_tuple(l, r, q);
        }
        return res;
    }

    /**
     * @brief Calculates vector of { l = min S, r = max S, (q[0], ..., q[k-1]) }, where S = { d | floor(vs[i] / d) = q[i] for i = 0,...,k-1 } in O(|vs| * Σ√v) time.
     * @tparam Container vector or array
     * @return vector of { l = min S, r = max S, (q[0], ..., q[k-1]) }, where S is { d | floor(vs[i] / d) = q[i] for i = 0,...,k-1 }.
     * It is guaranteed that l, r is ascending and q[i] is descending. 
     */
    template <typename Container, std::enable_if_t<std::is_integral_v<typename Container::value_type>, std::nullptr_t> = nullptr>
    std::vector<std::tuple<typename Container::value_type, typename Container::value_type, Container>> multiple_quotients_ranges(const Container& vs) {
        using T = typename Container::value_type;
        int n = vs.size();
        T max_val = *std::max_element(vs.begin(), vs.end());
        assert(*std::min_element(vs.begin(), vs.end()) >= 0);
        std::vector<std::tuple<T, T, Container>> res;
        for (T l = 1, r = 1; l <= max_val; l = r + 1) {
            Container qs{};
            if constexpr (std::is_same_v<Container, std::vector<T>>) qs.resize(n);
            r = std::numeric_limits<T>::max();
            for (int i = 0; i < n; ++i) {
                qs[i] = vs[i] / l;
                r = std::min(r, qs[i] == 0 ? std::numeric_limits<T>::max() : vs[i] / qs[i]);
            }
            res.emplace_back(l, r, std::move(qs));
        }
        return res;
    }
} // namespace suisen



#line 7 "test/src/number/enumerate_quotient/divide_both.test.cpp"

using namespace suisen;

constexpr int MAX = 1000010;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int l, r;
    std::cin >> l >> r;
    l = std::max(l - 1, 1);
    std::unordered_map<long long, long long> memo;
    auto f = [&memo](auto self, int l, int r) -> long long {
        const long long key = (long long) l * MAX + r;
        {
            auto it = memo.find(key);
            if (it != memo.end()) return it->second;
        }
        long long res = (long long) (r - l) * (r - l);
        for (auto [lg, rg, qs] : multiple_quotients_ranges(std::array<int, 2>{l, r})) {
            if (lg == 1 and rg == 1) continue;
            auto [nl, nr] = qs;
            res -= (rg - lg + 1) * self(self, nl, nr);
        }
        return memo[key] = res;
    };
    long long whole_pairs = (long long) (r - l) * (r - l);
    long long divisor_pairs = 0;
    for (auto [lg, rg, q] : quotient_ranges(r)) {
        divisor_pairs += (long long) std::max(0, (rg - std::max(lg - 1, l))) * q;
    }
    divisor_pairs += divisor_pairs - (r - l);
    std::cout << whole_pairs - f(f, l, r) - divisor_pairs << std::endl;
    return 0;
}
Back to top page