线段树(无名二)

文章描述了一种解决关于两个长度为n的序列a和b的查询问题的算法,通过构建树状结构并使用lazypropagation来优化1和2操作的时间复杂度。主要关注如何在O(logn)时间内处理区间修改和求和查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

两个长度均为n 的序列a 和b,其中b 是一个 1 到 n 的排列,a 一开始为全0 序列
现在有两种操作:
1 l r 表示a 中[l,r] 的数全部加1
2 l r 询问 [l,r]中的sum(a[i]/b[i]);这里sum表示求和;即i<=r;i>=l;每一个a[i]/b[i]的结果向下取整,然后再相加!

请你写一个程序能执行两个操作

Input

第一行两个正整数n,m 表示序列长度和操作数
接下来一行n 个整数表示bi;
接下来m 行每行包括每次操作的操作类型与操作区间;

(n,m<=1e5)

Output

对于每一种2 操作输出询问答案

Sample Input

5 5
1 3 2 4 5
1 1 3
2 1 5
1 2 4
1 2 3
2 2 4

Sample Output

1
2

思路:

如果按照单点修改,时间复杂度是o(m*nlongn)会超时。在每个节点,我们可以记录在当前节点下区间最少需要多少次1操作会使sum变化(记为mi)。因为每次只加一,所以不会出现上一次当前区间mi>0,这次mi<0.每次操作1时,当mi==0,说明需要改变sum,进行单点修改。遇到二输出即可。

代码:

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
using namespace std;
const int N = 1e5 + 10;
#define ls (o<<1)
#define rs (o<<1|1)
struct data
{
    int mi;//当前区间改变所需要1操作数
    int su;//和
}sum[N << 2];
long long  a[N];
int lazy[N << 2];
int n, m, b[N];
void pushupsum(int o)
{
    sum[o].su = sum[ls].su + sum[rs].su;
}
void pushupmi(int o)
{
    sum[o].mi = min(sum[ls].mi, sum[rs].mi);
}
void build(int o, int l, int r)//建树
{
    if (l == r)
    {
        sum[o].mi = b[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    pushupmi(o);
}
void  pushdown(int o, int l, int r)//下传lazy标记
{
    if (lazy[o])
    {
        int mid = (l + r) >> 1;
        sum[ls].mi -= lazy[o];
        sum[rs].mi -= lazy[o];
        lazy[ls] += lazy[o];
        lazy[rs] += lazy[o];
        lazy[o] = 0;
        return;
    }
}
void modify(int o, int l, int r)//单点修改
{
    if (sum[o].mi) return;
    if (l == r)
    {
        sum[o].su++;
        sum[o].mi = b[l];
        return;
    }
    pushdown(o, l, r);
    int mid = (l + r) / 2;
    modify(ls, l, mid);
    modify(rs, mid + 1, r);
    pushupsum(o);
    pushupmi(o);
}
void change(int o, int l, int r, int ql, int qr)//区间修改
{
    if (ql <= l && qr >= r)
    {
        sum[o].mi--;
        lazy[o]++;
        modify(o, l, r);
        return;
    }
    pushdown(o, l, r);
    int mid = (l + r) >> 1;
    if (ql <= mid)
        change(ls, l, mid, ql, qr);
    if (qr > mid)
        change(rs, mid + 1, r, ql, qr);
    pushupsum(o);
    pushupmi(o);
}
long long ask(int o, int l, int r, int ql, int qr)//求区间维护值
{
    if (ql <= l && qr >= r)
    {
        return sum[o].su;
    }
    pushdown(o, l, r);
    long long ans = 0;
    int mid = (l + r) >> 1;
    if (ql <= mid)
        ans += ask(ls, l, mid, ql, qr);
    if (qr > mid)
        ans += ask(rs, mid + 1, r, ql, qr);
    return ans;
}
int main()
{
    scanf("%d%d", &n, &m);
    memset(lazy, 0, sizeof lazy);
    for (int i = 1; i <= n; i++)
        scanf("%d", &b[i]);
    build(1, 1, n);
    for (int i = 1; i <= m; i++)
    {
        int k, ql, qr;
        scanf("%d", &k);
        if (k == 1)
        {
            scanf("%d%d", &ql, &qr);
            change(1, 1, n, ql, qr);
        }
        else
        {
            scanf("%d%d", &ql, &qr);
            printf("%lld\n", ask(1, 1, n, ql, qr));
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值