1. 程式人生 > >「NOIP模擬」奇襲【線段樹】【單調棧】

「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

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

所以我們考慮列舉 r r ,線段樹維護 m a x m i n + l max-min+l 的值,並且求出區間最小值與最小值個數。

在列舉 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

n e w m a x , n e w m i n newmax,newmin 的變化也是一段區間同時變化,所以我們用類似分治做法維護兩個單調棧,一個單調遞增,一個單調遞減。

由於改變是區間同時變化,所以根據單調棧的值來對線段樹進行區間修改。

由於使用了單調棧,所以根據時間複雜度計算原理,均攤得: O ( n l o g n ) O(nlog_n)

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#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<r) {
		ll mid=l+r>>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]<a[up[cup-1]]) {
      		modify(1,1,n,up[cup-2]+1,up[cup-1],a[up[cup-1]]);
      		cup--;
   		}
    	
		while(cdown>=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;
}