Naive Operations (线段树 分析复杂度)

17 篇文章 0 订阅
4 篇文章 0 订阅
这篇博客讲解了一道涉及线段树的数据结构问题,通过分析b数组的排列特性,提出利用imin数组记录还需修改次数的方法来高效处理区间加1和查询a[i]/b[i]和的问题。作者详细介绍了如何构建线段树、维护区间最小值以及处理lazy标记,确保在1e6级别的查询中达到nlogn的时间复杂度。
摘要由CSDN通过智能技术生成

题目:
给出一个长度为n初值为0的数组,以及长度为n的b数组,然后q次操作,add(l,r) 使得区间l-r所有元素+1,或者查询l~r区间a[i]/b[i]的和
题解:
很少情况下能很快写出一道线段树题目,关键还0调试
首先题目说了b是个排列,突破点也就是b这个条件。
因为题目是向下取整,那么假设b[1]=100000,那么你对他修改99999次都是没有任何影响的,那岂不是可以不去修改这个点。同理考虑其他的节点,发现b[i]越大,那么a[i]/b[j]如果变大的话,需要修改的次数也会越多。
我们计算一下这个次数
在这里插入图片描述级别是一个1e6的级别,nlogn可以过。

那么我们用一个 i m i n i imin_i imini表示你还需要对 a i a_i ai加几次,才会使得 a i / b i a_i/b_i ai/bi增大1.
如果imin用最小值去维护区间,每次区间加的时候如果发现区间最小值为1,那么递归下去修改贡献,否则直接懒标记把区间最小值-1。

代码:

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int maxn = 1e5 + 10;
const int mod=998244353;

int b[maxn];
int imin[maxn<<2],sum[maxn<<2],lazy[maxn<<2];

void push_up(int node){
    imin[node]=min(imin[node<<1],imin[node<<1|1]);
    sum[node]=sum[node<<1]+sum[node<<1|1];
}

void build(int node,int start,int ends){
    lazy[node]=0;
    if(start==ends){
        sum[node]=0;
        imin[node]=b[start];
        return ;
    }
    int mid=(start+ends)>>1;
    build(node<<1,start,mid);
    build(node<<1|1,mid+1,ends);
    push_up(node);
}

void push_down(int node){
    if(lazy[node]){
        lazy[node<<1]+=lazy[node];
        lazy[node<<1|1]+=lazy[node];
        imin[node<<1]-=lazy[node];
        imin[node<<1|1]-=lazy[node];
    }
    lazy[node]=0;
}

void update(int node,int start,int ends,int l,int r){
    if(start==ends){
        if(imin[node]==1){
            imin[node]=b[start];
            sum[node]++;
        }
        else imin[node]--;
        return ;
    }
    if(l<=start&&ends<=r&&imin[node]!=1){
        lazy[node]+=1;
        imin[node]--;
        return ;
    }
    push_down(node);
    int mid=start+ends>>1;
    if(l<=mid) update(node<<1,start,mid,l,r);
    if(mid<r) update(node<<1|1,mid+1,ends,l,r);
    push_up(node);
}

int query(int node,int start,int ends,int l,int r){
    if(l<=start&&ends<=r){
        return sum[node];
    }
    int mid=start+ends>>1;
    push_down(node);
    int res=0;
    if(l<=mid) res+=query(node<<1,start,mid,l,r);
    if(mid<r) res+=query(node<<1|1,mid+1,ends,l,r);
    return res;
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,q;

    while(cin>>n>>q){
        for(int i=1;i<=n;i++) cin>>b[i];
        build(1,1,n);
        while(q--){
            string s;
            cin>>s;
            if(s[0]=='a'){
                int x,y;
                cin>>x>>y;
                update(1,1,n,x,y);
            }
            else{
                int x,y;
                cin>>x>>y;
                int ans=query(1,1,n,x,y);
                cout<<ans<<endl;
            }
        }
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值