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,常数有点大。
正在学离线做法。