「NOIP模拟」奇袭【线段树】【单调栈】

题意:

给定数列,求有多少个区间满足区间最大+1-区间最小=区间长度

满足条件为:
m a x + 1 − m i n = r + 1 − l = > m a x − m i n = r − l max+1-min=r+1-l=>max-min=r-l max+1min=r+1l=>maxmin=rl

m a x − m i n + l = r max-min+l=r maxmin+l=r

所以我们考虑枚举 r r r,线段树维护 m a x − m i n + l max-min+l maxmin+l的值,并且求出区间最小值与最小值个数。

在枚举 r r r的过程中,当前区间再加上 r r r后的新区间的 n e w m a x ≥ m a x , n e w m i n ≤ m i n newmax\geq max,newmin\leq min newmaxmax,newminmin

n e w m a x , n e w m i n newmax,newmin newmax,newmin的变化也是一段区间同时变化,所以我们用类似分治做法维护两个单调栈,一个单调递增,一个单调递减。

由于改变是区间同时变化,所以根据单调栈的值来对线段树进行区间修改。

由于使用了单调栈,所以根据时间复杂度计算原理,均摊得: O ( n l o g n ) O(nlog_n) O(nlogn)

#include 
#include 
#include 
#include 
#include 
#define db double
#define sg string
#define ll long long
#define rel(i,x,y) for(ll i=(x);i<(y);i++)
#define rep(i,x,y) for(ll i=(x);i<=(y);i++)
#define red(i,x,y) for(ll i=(x);i>=(y);i--)
#define res(i,x) for(ll i=head[x];i;i=nxt[i])
using namespace std;

const ll N=5e4+5;
const ll Inf=1e18;
const ll Mod=1e9+7;
const db Eps=1e-10;

ll n,m,ans,a[N],up[N<<2],down[N<<2],mn[N<<2],cnt[N<<2],lazy[N<<2];

#define lson (p<<1)
#define rson (p<<1|1)

inline ll read() {
	ll x=0;char ch=getchar();bool f=0;
	while(ch>'9'||ch<'0'){if(ch=='-')f=1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return f?-x:x;
}

void maketree(ll p,ll l,ll r) {
	mn[p]=l;cnt[p]=1;
	if(l>1;
		maketree(lson,l,mid);
		maketree(rson,mid+1,r);
	}
}

void pushdown(ll p) {
	if(lazy[p]) {
		lazy[lson]+=lazy[p];
		lazy[rson]+=lazy[p];lazy[p]=0;
	}
}

void update(ll p) {
	ll a=mn[lson]+lazy[lson];
	ll b=mn[rson]+lazy[rson];
	mn[p]=min(a,b);
	cnt[p]=(a==mn[p]?cnt[lson]:0)+(b==mn[p]?cnt[rson]:0);
}

void modify(ll p,ll l,ll r,ll x,ll y,ll z) {
	if(x<=l&&r<=y) {
		lazy[p]+=z;return ;
	}
	
	pushdown(p);
	
	ll mid=l+r>>1;
	
	if(x<=mid) modify(lson,l,mid,x,y,z);
	if(y>mid) modify(rson,mid+1,r,x,y,z);update(p);
}

void File() {
	freopen("raid.in","r",stdin);
	freopen("raid.out","w",stdout);
}

int main() {
	File();
	
	n=read();
	
	memset(a,-1,sizeof(a));
	
	rep(i,1,n) {
		ll x=read(),y=read();a[x]=y;
	}
	
	maketree(1,1,n);
	
  	ll cup=1,cdown=1;up[0]=-1;down[0]=-1;
  	
  	rep(i,1,n) {
  		while(cup>=2&&a[i]=2&&a[i]>a[down[cdown-1]]) {
     		modify(1,1,n,down[cdown-2]+1,down[cdown-1],-a[down[cdown-1]]);
      		cdown--;
    	}	
    	up[cup++]=i;down[cdown++]=i;
    	
    	modify(1,1,n,up[cup-2]+1,up[cup-1],-a[up[cup-1]]);
    	modify(1,1,n,down[cdown-2]+1,down[cdown-1],a[down[cdown-1]]);ans+=cnt[1];
	}
  
  	printf("%lld\n",ans);

	return 0;
}

你可能感兴趣的:(NOIP及模拟,数据结构-(单调)栈,数据结构-线段树)