CF 920F 区间求约数个数
题目大意
给定一个序列,支持区间每个元素变为它们的约数个数、区间求和
\(n \leq 10^5,a_i \leq 10^9\)
思路
由于\(a_i \leq 10^9\),因此每一个元素最多会被修改有限次就会变为1或2,复杂度\(O(nlogn)\)
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 3e5 + 10;
const int M = 1e6 + 10;
int a[N];
int Ans[M];
void sieve () {
for(int i = 1;i <= M - 10; i ++) {
for(int j = i;j <= M - 10;j += i) {
Ans[j] ++;
}
}
}
struct node {
int l,r;
ll sum;
int tag;
}t[N << 2];
void update(int rt) {
int ch = rt << 1;
t[rt].sum = t[ch].sum + t[ch + 1].sum;
t[rt].tag = (t[ch].tag && t[ch + 1].tag);
}
void build(int l,int r,int rt) {
t[rt].l = l;
t[rt].r = r;
if(l == r) {
t[rt].sum = a[l];
t[rt].tag = (a[l] <= 2); //是质数为1,意味着不可以继续进行分解
return;
}
int mid = (l + r) >> 1;
int ch = rt << 1;
build(l,mid,ch);
build(mid + 1,r,ch + 1);
update(rt);
}
void modify(int l,int r,int rt) {
if(t[rt].tag) return;
if(t[rt].l == t[rt].r) {
t[rt].sum = Ans[t[rt].sum];
t[rt].tag = (t[rt].sum <= 2);
return;
}
int mid = (t[rt].l + t[rt].r) >> 1;
int ch = rt << 1;
if(r <= mid) {
modify(l,r,ch);
}else if(l > mid) {
modify(l,r,ch + 1);
}else {
modify(l,mid,ch);
modify(mid + 1,r,ch + 1);
}
update(rt);
}
ll query(int l,int r,int rt) {
if(l == t[rt].l and r == t[rt].r) return t[rt].sum;
int mid = (t[rt].l + t[rt].r) >> 1;
int ch = rt << 1;
ll res = 0;
if(r <= mid) {
res += query(l,r,ch);
}else if(l > mid) {
res += query(l,r,ch + 1);
}else {
res += query(l,mid,ch) + query(mid + 1,r,ch + 1);
}
//cout << "#:" << l << ' ' << r << ' ' << res << endl;
return res;
}
int n,m;
int main () {
sieve();
scanf("%d %d",&n,&m);
for (int i = 1;i <= n; i ++) {
scanf("%d",&a[i]);
}
build(1,n,1);
while(m --) {
int t,l,r;
scanf("%d %d %d",&t,&l,&r);
if(t == 1) {
modify(l,r,1);
}
else {
printf("%lld\n",query(l,r,1));
}
}
return 0;
}
/*
7 6
6 4 1 10 3 2 4
2 1 7
2 4 5
1 3 5
2 4 4
1 5 7
2 1 7
*/