题目链接
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();
}
}