0%

CF920F SUM and REPLACE

传送门

Solution

对于 1ai1061\le a_i \le 10^6,不超过 6 次操作可将一个 aia_i 变为 1122。有 O(nlogn)O(n\log n) 的方法预处理出 11061\sim 10^6 的正约数个数。

对于操作 1,暴力修改该区间。当 ai=1a_i=1ai=2a_i=2 时,视 aia_i 不可用。设 FiF_i 表示点 ii 左边最近的可用点(包括 ii 自己)。初始化 Fi=iF_i=i。当 ii 不可用后,令 Fi=find(i1)F_i=find(i-1),其中函数 find()find() 为并查集的查找函数。这样就实现了类似链表的遍历和删除操作,总共的修改次数不超过 6n6n,可以接受。用树状数组维护区间和,暴力修改时直接更新。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll N = 3e5 + 10;

ll n, m, a[N], mp[1000010], fa[N], tr[N];

ll find(ll x) {return fa[x] == x ? x : fa[x] = find(fa[x]);}

ll lowbit(ll x) {return x & -x;}

void add(ll x, ll c)
{
for (; x <= n; x += lowbit(x))
tr[x] += c;
}

ll query(ll x)
{
ll res = 0;
for (; x > 0; x -= lowbit(x))
res += tr[x];
return res;
}

int main()
{
for (int i = 1; i <= 1e6; i++)
for (int j = i; j <= 1e6; j += i)
mp[j]++;
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
add(i, a[i]);
fa[i] = i;
if (a[i] == 1 || a[i] == 2)
fa[i]--;
}
while (m--) {
ll opt, l, r;
scanf("%lld%lld%lld", &opt, &l, &r);
if (opt == 1) {
ll p = find(r);
while (p >= l) {
add(p, -a[p]);
a[p] = mp[a[p]];
add(p, a[p]);
if (a[p] == 1 || a[p] == 2)
fa[p] = find(p - 1);
p = find(p - 1);
}
} else
printf("%lld\n", query(r) - query(l - 1));
}
return 0;
}