CF920F SUM and REPLACE 题解
线段树好题。
首先,如果做过 P4145 上帝造题的七分钟2 / 花神游历各国 ,那么这道题还是十分好想的。
我们需要意识到一个问题:对于 \(1,2\) 这两个数而言,我们对它们修改是没有意义的,\(1\) 还是 \(1\),\(2\) 还是 \(2\)。
那么我们再想想,对于 \(a_i\) 几次操作之后就会变成 \(1,2\) 呢?
我们可以 使用数学方法 打一个暴力程序算一算,发现最多不超过 6 次。
于是这不就变成上面那道题了吗?
我们先预处理出 \(d_i\),然后线段树维护两个值 \(sum,max\),在修改操作的时候如果 \(max\) 为 \(1\) 或 \(2\),那么跳过不修改;否则直接暴力修改即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL Max(LL fir, LL sec) {return (fir > sec) ? fir : sec;}
const int MAXN = 3e5 + 10, MAXA = 1e6 + 10;
int n, m, a[MAXN], cnt[MAXA];
struct node
{
int l, r;
LL sum, maxn;
#define l(p) tree[p].l
#define r(p) tree[p].r
#define s(p) tree[p].sum
#define m(p) tree[p].maxn
}tree[MAXN << 2];
int read()
{
int sum = 0, fh = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') fh = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {sum = (sum << 3) + (sum << 1) + (ch ^ 48); ch = getchar();}
return sum * fh;
}
void build(int p, int l, int r)
{
l(p) = l, r(p) = r;
if (l == r) {s(p) = m(p) = a[l]; return ;}
int mid = (l(p) + r(p)) >> 1;
build(p << 1, l, mid); build(p << 1 | 1, mid + 1, r);
s(p) = s(p << 1) + s(p << 1 | 1);
m(p) = Max(m(p << 1), m(p << 1 | 1));
}
void change(int p, int l, int r)
{
if (l(p) == r(p)) {s(p) = m(p) = cnt[s(p)]; return ;}
if (m(p) == 2 || m(p) == 1) return ;
int mid = (l(p) + r(p)) >> 1;
if (l <= mid) change(p << 1, l, r);
if (r > mid) change(p << 1 | 1, l, r);
s(p) = s(p << 1) + s(p << 1 | 1);
m(p) = Max(m(p << 1), m(p << 1 | 1));
}
LL ask(int p, int l, int r)
{
if (l(p) >= l && r(p) <= r) return s(p);
int mid = (l(p) + r(p)) >> 1; LL val = 0;
if (l <= mid) val += ask(p << 1, l, r);
if (r > mid) val += ask(p << 1 | 1, l, r);
return val;
}
int main()
{
n = read(), m = read();
for (int i = 1; i <= n; ++i) a[i] = read();
build(1, 1, n);
for (int i = 1; i <= 1000000; ++i)
for (int j = i; j <= 1000000; j += i)
cnt[j]++;
for (int i = 1; i <= m; ++i)
{
int opt = read(), l = read(), r = read();
if (opt == 1) change(1, l, r);
else printf("%lld\n", ask(1, l, r));
}
return 0;
}