cf1567E Non-Decreasing Dilemma套路线段树

这篇博客介绍了如何使用线段树解决区间内递增子数组的个数查询与单点修改问题。作者通过分析题意,提出维护区间最左侧递增个数(lc)、最右侧递增个数(rc)等信息,并详细阐述了线段树节点的pushup操作。在pushup过程中,作者考虑了不同情况下的边界条件和状态更新。最后,博主分享了代码实现和复杂度分析,强调了多做题积累经验的重要性。
摘要由CSDN通过智能技术生成
题目链接

https://codeforces.com/contest/1567/problem/E

题意

维护数据结构,要求支持单点修改,区间查询递增子数组(连续)个数。

思路

一眼就很套路的线段树题目。
单点修改不谈,直接考虑如何维护答案。

首先明确长度为k的递增数组,答案是1累加到k,为方便起见,我们记录k的累加为s_k。
考虑答案的pushup部分,如果左区间最右大于右区间最左,那么答案就是左子树和右子树的和,否则的话,我们需要算上新增的情况。具体来说,如果左边区间最右有rc个递增,右区间最左有lc个递增,那么整个区间答案应该是左右区间答案和减去(s_rc+s_lc),再加上s_(rc+lc);

也就是说,我们需要维护的信息有:区间最左侧递增个数lc,右侧的rc,最左侧值vall,最右侧值valr,区间答案ans。

ans,vall,valr很好维护,我们考虑下lc和rc。当pushup时,lc可以直接继承左区间的lc,rc继承右区间的rc。这时要注意,如果左区间最右小于等于右区间最左,这时如果左区间lc等于左区间长,那么总区间的lc还需要加上右区间lc,总区间的rc也是类似的。因此我们还需要维护一个len值代表维护区间长度,当然这个len直接用区间的lr计算也是可以的。

还有一个小小的细节需要注意,我们ans的pushup是可以直接根据lc和rc维护,但如果是query函数里,查询的区间长度不一定覆盖整个区间。比如长为6的区间,划分是1-3,4-6,我们查询1-4,那么最后我们需要合并1-3和4-4两个区间答案,这时假如4-6区间的lc是2,是比我们需要的区间4-4长的,因此我们对他需要和区间长取一个最小值。

最后记得开long long
复杂度

教训/收获

蓝书见过类似的维护方法,还是得多做题学习下常用套路啊~
(这场cf赛后发现EF全能做,还都是一两遍过,这要是时间足够,不是上大分。)

代码
#include<cstdio>
#include<iostream>
#include<set>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#include<bitset>
#include<complex>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=400505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
	int a[maxn];
    struct T{
        int l,r,ans,lc,rc,len,vall,valr;
    }t[maxn<<2];
    int ini[maxn];

    void pushup(int rt){
        t[rt].ans=t[rt<<1].ans+t[rt<<1|1].ans;
        t[rt].lc=t[rt<<1].lc;
        t[rt].rc=t[rt<<1|1].rc;
        t[rt].vall=t[rt<<1].vall;
        t[rt].valr=t[rt<<1|1].valr;
        t[rt].len=t[rt<<1].len+t[rt<<1|1].len;
        if(t[rt<<1].valr<=t[rt<<1|1].vall){
            t[rt].ans=t[rt].ans-ini[t[rt<<1].rc]-ini[t[rt<<1|1].lc]+ini[t[rt<<1].rc+t[rt<<1|1].lc];
            if(t[rt<<1].lc==t[rt<<1].len)   t[rt].lc+=t[rt<<1|1].lc;
            if(t[rt<<1|1].rc==t[rt<<1|1].len)   t[rt].rc+=t[rt<<1].rc;
        }
    }
    void build(int rt,int l,int r){
        t[rt].l=l,t[rt].r=r;
        if(l==r){
            t[rt].ans=1,t[rt].lc=t[rt].rc=1;
            t[rt].len=1;
            t[rt].vall=t[rt].valr=a[l];
            return;
        }
        int mid=l+r>>1;
        build(rt<<1,l,mid);
        build(rt<<1|1,mid+1,r);
        pushup(rt);
        //cout<<l<<' '<<r<<' '<<t[rt].ans<<endl;
    }
    void update(int rt,int x,int y){
        if(t[rt].l==t[rt].r){
            t[rt].vall=t[rt].valr=y;
            return ;
        }
        int mid=t[rt].l+t[rt].r>>1;
        if(x<=mid)  update(rt<<1,x,y);
        else        update(rt<<1|1,x,y);
        pushup(rt);
        //cout<<"change"<<t[rt].l<<' '<<t[rt].r<<' '<<t[rt].lc<<' '<<t[rt].rc<<endl;
    }
    int query(int rt,int l,int r){
        if(l<=t[rt].l&&r>=t[rt].r){
           // cout<<t[rt].l<<' '<<t[rt].r<<' '<<t[rt].ans<<endl;
            return t[rt].ans;
        }
        int mid=t[rt].l+t[rt].r>>1;
        int ans=0;
        if(l<=mid)  ans+=query(rt<<1,l,r);
        if(r>mid)  ans+=query(rt<<1|1,l,r);
        if(l<=mid&&r>mid)
            if(t[rt<<1].valr<=t[rt<<1|1].vall){
                int lcc=min(t[rt<<1].rc,mid-l+1);
                int rcc=min(t[rt<<1|1].lc,r-mid);
               // cout<<t[rt<<1].rc<<' '<<t[rt<<1|1].lc<<endl;
               // cout<<lcc<<' '<<rcc<<endl;
                ans=ans-ini[lcc]-ini[rcc]+ini[lcc+rcc];
            }
        //cout<<t[rt].l<<' '<<t[rt].r<<' '<<ans<<endl;
        return ans;
    }
	void solve(){
        cin>>n>>m;
        int op,x,y;
        for(int i=1;i<=n;i++)
            cin>>a[i];
        build(1,1,n);
        while(m--){
            cin>>op>>x>>y;
            if(op==1)   update(1,x,y);
            else    cout<<query(1,x,y)<<endl;
        }
	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		//cin>>tn;
        ini[1]=1;
        for(int i=2;i<maxn;i++)
            ini[i]=ini[i-1]+i;
		for(int __=1;__<=tn;__++){
			solve();
		}
	} 
	
						
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值