Let D(x) be the number of positive divisors of a positive integer x. For example, D(2) = 2 (2 is divisible by 1 and 2), D(6) = 4 (6 is divisible by 1, 2, 3 and 6).
You are given an array a of n integers. You have to process two types of queries:
REPLACE l r — for every replace ai with D(ai);
SUM l r — calculate .
Print the answer for each SUM query.
Input
The first line contains two integers n and m (1 ≤ n, m ≤ 3·105) — the number of elements in the array and the number of queries to process, respectively.
The second line contains n integers a1, a2, …, an (1 ≤ ai ≤ 106) — the elements of the array.
Then m lines follow, each containing 3 integers ti, li, ri denoting i-th query. If ti = 1, then i-th query is REPLACE li ri, otherwise it’s SUM li ri (1 ≤ ti ≤ 2, 1 ≤ li ≤ ri ≤ n).
There is at least one SUM query.
Output
For each SUM query print the answer to it.
Example
Input
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
Output
30
13
4
22
题意:两种操作
操作一: 将区间[l,r]的每个数都变为其因子的个数.
操作二:求区间[l,r] 所有数字的和.
分析:
刚开始看到这个题目,一直在想 要维护什么东西,可以用lazy 来处理。 想了半天还是无果 。有一个很显然的结论就是 转换过一定次数之后一定为 2 ,然后想着先看看 每个数字大概可以转换几次可以到2, 暴力跑了一下,发现在6次左右。 这个范围就很小,所以 我们一边维护区间的和,一边维护区间的最大值,对于区间更新操作来说,如果当前区间的最大值就是2,那么这个区间一定不用在更新了。这样的话查询操作复杂度log,更新的话 刚开始要一个个更新,但是 最多把每个数字都变6次,之后都是定值,在进行更新操作就不用真的变化,所以更新时间上也是是足够的。
心得 :对于线段树 的更新操作,如果数据更新到一定的次数就不会变化了,那么我们可以维护其最大值来判定是否到达定值,然后来决定是否继续更新。然后对于未到达定值的时候,我们遍历区间一个个更新。
代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int N = 1e6+11;
const int M = 3e5+11;
const int inf =0x3f3f3f3f;
const int mod = 1e9+7;
const int inff = 0x3f3f3f3f3f3f3f3f;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3) +ch-'0';ch=getchar();}
return x*f;
}
/*-------------------------------------*/
struct Tree{
int l,r;
LL sum,mx;
}tree[M<<2];
int cnt[N];
void init(){
for(int i=1;i<N;i++){
for(int j=i;j<N;j+=i){
cnt[j]++;
}
}
/* // 暴力跑跑
int ans=0;
for(int i=1;i<N;i++){
int t=0;int z=i;
while(z>2){ t++; z=cnt[z]; }
ans=max(ans,t);
}
cout<<ans<<endl; // 最大的变换次数为 6
*/
}
void Up(int o){
tree[o].sum=tree[o<<1].sum+tree[o<<1|1].sum;
tree[o].mx=max(tree[o<<1].mx,tree[o<<1|1].mx);
}
void Build(int o,int l,int r){
tree[o].l=l; tree[o].r=r;
if(l==r){
int a; scanf("%d",&a);
tree[o].sum=tree[o].mx=a;
return ;
}
int mid=(l+r)>>1;
Build(o<<1,l,mid);
Build(o<<1|1,mid+1,r);
Up(o);
}
void UpDate(int o,int le,int ri){
if(tree[o].mx<=2) return ; // 关键 ,本区间就不用动了。
if(tree[o].l==tree[o].r) { // 遍历区间 一个个改变
tree[o].mx=tree[o].sum=cnt[tree[o].sum];
return ;
}
int mid = (tree[o].l+tree[o].r)>>1;
if(ri<=mid ) UpDate(o<<1,le,ri);
else if(le>mid) UpDate(o<<1|1,le,ri);
else {
UpDate(o<<1,le,mid);
UpDate(o<<1|1,mid+1,ri);
}
Up(o);
}
LL Query(int o,int le,int ri){
if(tree[o].l>=le&&tree[o].r<=ri) return tree[o].sum;
int mid =(tree[o].l+tree[o].r)>>1;
if(ri<=mid ) return Query(o<<1,le,ri);
else if(le>mid) return Query(o<<1|1,le,ri);
else {
return Query(o<<1,le,mid)+Query(o<<1|1,mid+1,ri);
}
}
int main(){
init();
int n,m; scanf("%d%d",&n,&m);
Build(1,1,n);
int op,a,b;
while(m--){
scanf("%d%d%d",&op,&a,&b);
if(op==1) UpDate(1,a,b);
else {
printf("%lld\n",Query(1,a,b));
}
}
return 0;
}