Codeforces Round 1017 (Div. 4)

Codeforces Round 1017 (Div. 4)

只写了\(H\),别的有空再补。

H.

题目大意

  • q次询问,每次给三个数\(k,l,r\),输出\(f(k, l, r)\)
    \(f\)函数如下
int f(int k, int l, int r) {
    int res = 0;
    for (int i = l; i <= r; i++) {
        while (k % a[i] == 0) {
            k /= a[i];
        }
        res += k;
    }
    return res;
}

解题思路

  • 直接暴力枚举区间\([l,r]\)的话,时间复杂度\(O(t\times q\times n)\),必超时。
  • 观察发现,对于区间内的某个数\(x\),如果是\(k\)的因子,只会在第一次出现的地方去操作\(k\),后面就不会是\(k\)的因子了。
  • 所以提前存储下来每个数出现的位置,需要时,直接二分查找某个数在区间内第一次出现的位置即可。提前预处理,优化掉枚举的过程。
  • 对于\(k\)\(\sqrt{k}\)求出\(k\)的所有因子,查找在区间\([l,r]\)第一次出现某个因子的位置并存下来。
  • 遍历一遍能让\(k\)产生变化的位置(就是上一步存好的信息),操作完\(k\),累加答案。
  • 相邻两个操作位置之间,每个位置对答案的累加都是前一个操作完之后的\(k\)

AC代码

#include<bits/stdc++.h>

using i64 = long long;

void Main() {
    int n, q;
    std::cin >> n >> q;
    std::vector<int> a(n);
    std::map<int, std::vector<int>> pos;
    for (int i = 0; i < n; i++) {
        std::cin >> a[i];
        pos[a[i]].push_back(i);
    }
    while (q--) {
        int k, l, r;
        std::cin >> k >> l >> r;
        l--, r--;
        std::vector<int> fac;
        for (int i = 1; i * i <= k; i++) {
            if (k % i == 0) {
                fac.push_back(i);
                if (i * i != k) {
                    fac.push_back(k / i);
                }
            }
        }
        std::vector<std::pair<int, int>> b;
        for (auto i : fac) {
            auto it = std::lower_bound(pos[i].begin(), pos[i].end(), l);
            if (it != pos[i].end() && *it <= r) {
                b.push_back({*it, i});
            }
        }
        if (b.empty()) {
            std::cout << 1LL * (r - l + 1) * k << "\n";
            continue;
        }
        sort(b.begin(), b.end());
        i64 ans = 0;
        if (b[0].first != l) {
            ans += 1LL * (b[0].first - l) * k;
        }
        for (int i = 0; i < b.size(); i++) {
            if (i > 0) {
                ans += 1LL * (b[i].first - b[i - 1].first - 1) * k;
            }
            while (k % b[i].second == 0) {
                k /= b[i].second;
            }
            ans += k;
        }
        if (b.back().first != r) {
            ans += 1LL * (r - b.back().first) * k;
        }
        std::cout << ans << "\n";
    }
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), std::cout.tie(0);
    int T = 1;
    std::cin >> T;
    while (T--) {
        Main();
    }
    return 0;
}

这个做法带map和sort,常数有点大。
正在学离线做法。

posted @ 2025-04-15 16:39  sunjiànqiáng  阅读(54)  评论(0)    收藏  举报