【CF997E】Good Subsegments(线段树)(单调栈)

传送门


题解:

由于保证给出的是一个排列,我们考虑在这上面做一点文章。

显然这种情况下连续段 [ l , r ] [l,r] [l,r]一定有 m a x [ l : r ] − m i n [ l : r ] = r − l max[l:r]-min[l:r]=r-l max[l:r]min[l:r]=rl

考虑离线,将所有询问按照右端点排序,每一个合法的区间在左端点+1。

维护区间 m a x [ l : r ] − m i n [ l : r ] − ( r − l ) max[l:r]-min[l:r]-(r-l) max[l:r]min[l:r](rl)的最小值和最小值出现次数(显然这个东西是恒非负的),则我们我们一边平移右端点一边用单调栈维护区间 m a x max max m i n min min的变化,显然 [ r : r ] [r:r] [r:r]这个区间的值是 0 0 0,那么全局最小值就是 0 0 0,当前全局最小值出现次数就是右端点为 r r r的合法区间个数。为了计算所有合法区间我们维护一下历史最小值出现次数之和就行了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;
using pii=std::pair<int,int>;
#define fi first
#define se second

cs int N=1.2e5+7;

ll sum[N<<2];
int cnt[N<<2],mn[N<<2],tim[N<<2],tag[N<<2];

#define lc u<<1
#define rc lc|1

inline void build(int u,int l,int r){
	mn[u]=l,cnt[u]=1;if(l==r)return ;
	int mid=l+r>>1;
	build(lc,l,mid);build(rc,mid+1,r);
}
inline void add_v(int u,int ad){mn[u]+=ad,tag[u]+=ad;}
inline void add_t(int u,int ad){tim[u]+=ad,sum[u]+=(ll)ad*cnt[u];}
inline void pushdown(int u){
	if(tag[u]){
		add_v(lc,tag[u]);
		add_v(rc,tag[u]);
		tag[u]=0;
	}
	if(tim[u]){
		if(mn[lc]==mn[u])add_t(lc,tim[u]);
		if(mn[rc]==mn[u])add_t(rc,tim[u]);
		tim[u]=0;
	}
}
inline void pushup(int u){
	mn[u]=std::min(mn[lc],mn[rc]);
	sum[u]=sum[lc]+sum[rc];
	cnt[u]=(mn[lc]==mn[u]?cnt[lc]:0)+(mn[rc]==mn[u]?cnt[rc]:0);
}
inline void modify(int u,int l,int r,int ql,int qr,int val){
	if(ql<=l&&r<=qr)return add_v(u,val);
	pushdown(u);int mid=l+r>>1;
	if(ql<=mid)modify(lc,l,mid,ql,qr,val);
	if(mid<qr)modify(rc,mid+1,r,ql,qr,val);
	pushup(u);
}
inline ll query(int u,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr)return sum[u];
	pushdown(u);int mid=l+r>>1;
	if(qr<=mid)return query(lc,l,mid,ql,qr);
	if(mid<ql)return query(rc,mid+1,r,ql,qr);
	return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
}

#undef lc 
#undef rc

ll ans[N];int n,m;
int a[N],tp_mx,stk_mx[N],tp_mn,stk_mn[N];
std::vector<pii> vec[N];

signed main(){
#ifdef zxyoi
	freopen("good_segment.in","r",stdin);
#endif
	n=gi();build(1,1,n);
	for(int re i=1;i<=n;++i)a[i]=gi();
	m=gi();for(int re i=1;i<=m;++i){
		int l=gi(),r=gi();
		vec[r].push_back(pii(l,i));
	}
	for(int re i=1;i<=n;++i){
		add_v(1,-1);
		while(tp_mx&&a[stk_mx[tp_mx]]<a[i])
		modify(1,1,n,stk_mx[tp_mx-1]+1,stk_mx[tp_mx],a[i]-a[stk_mx[tp_mx]]),--tp_mx;
		while(tp_mn&&a[stk_mn[tp_mn]]>a[i])
		modify(1,1,n,stk_mn[tp_mn-1]+1,stk_mn[tp_mn],a[stk_mn[tp_mn]]-a[i]),--tp_mn;
		stk_mx[++tp_mx]=stk_mn[++tp_mn]=i;add_t(1,1);
		for(pii t:vec[i])ans[t.se]=query(1,1,n,t.fi,i);
	}
	for(int re i=1;i<=m;++i)cout<<ans[i]<<"\n";
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值