【LuoguP4145】上帝造题的七分钟2(线段树区间开平方根)

线段树优化
本文介绍了一种使用线段树的数据结构解决区间开平方问题的方法。通过维护区间最大值避免不必要的运算,实现单点修改,时间复杂度达到nlogn。适用于较大数值多次开平方后接近1的情况。

题目大意

线段树,支持

  1. 对区间内每个数开平方2,下去整。
  2. 区间和查询

题目链接

传送门

分析

由于是对每个数取平方根,这样的懒标签是在是不好打,只好考虑单点修改了,时间复杂度为nlogn,n最大是10的5次方,勉强可以接受。这里我们多维护一个区间最大值,如果区间最大值是1的话就不用修改了,实际上,如果对一个较大的数反复开根号的话是不需要几次的就可以开到1,所以这个最大值的维护可以帮我门在操作较多时省区不少没必要的运算。思路和上一题一样,就是一些局部优化。
要注意这题l可能大于r,这时候题目要求l和r交换,不要看漏。

上一题链接

代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cctype>
#include <cmath>
#define mid (l + r >> 1)

using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, m;

ll read(){
    ll x = 0, op = 1;
    char ch = getchar();
    while (!isdigit(ch)){
        if (ch == '-') op = -1;
        ch = getchar();
    }
    while (isdigit(ch)){
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * op;
}

ll sum[N << 2], Max[N << 2];

inline void push_up(int i){
    sum[i] = sum[i << 1] + sum[i << 1|1];
    Max[i] = max(Max[i << 1], Max[i << 1|1]);
}

inline void buildTree(int i, int l, int r){
    if (l == r){
        sum[i] = Max[i] = read();
        return;
    }

    buildTree(i << 1, l, mid);
    buildTree(i << 1|1, mid + 1, r);
    push_up(i);
}

inline void modify_square(int i, int l, int r, int crtl, int crtr){
    if (crtl > crtr)
        swap(crtl, crtr);

    if (Max[i] <= 1)
        return;

    if (l == r){
        sum[i] = sqrt(sum[i]);
        Max[i] = sqrt(Max[i]);
        return;
    }

    if (mid >= crtl)
        modify_square(i << 1, l, mid, crtl, crtr);
    if (mid < crtr)
        modify_square(i << 1|1, mid + 1, r, crtl, crtr);

    push_up(i);
}

ll query_interval(int i, int l, int r, int crtl, int crtr){
    if (crtl > crtr)
        swap(crtl, crtr);

    if (l >= crtl && r <= crtr)
        return sum[i];

    ll res = 0;
    if (mid >= crtl)
        res += query_interval(i << 1, l, mid, crtl, crtr);
    if (mid < crtr)
        res += query_interval(i << 1|1, mid + 1, r, crtl, crtr);

    return res;
}

int main() {
    n = read();
    buildTree(1, 1, n);
    m = read();
    int a = 0, b = 0, op = 0;
    while (m--){
        op = read(), a = read(), b = read();
        if(op == 0)
            modify_square(1, 1, n, a, b);
        else
            printf("%lld\n",query_interval(1, 1, n, a, b));
    }
    return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值