【CodeForces - 920F 】SUM and REPLACE 【线段树的区间操作+思维】

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值