Continuous Intervals(单调栈+线段树)

6 篇文章 0 订阅
3 篇文章 0 订阅

题目地址
题目大意:定义一个美丽区间是指将一个区间排序之后任意相邻两个数的差的绝对值不超过1,然后给你 n n n个数,问你美丽区间的数量。
Solution:

  1. 对于这种区间计数类问题,我们有一个比较明确的方向就是固定一个端点然后查看另一个端点的合法情况有多少个。
  2. 我们发现如果一个区间合法,那么这个区间一定满足 m a x n − m i n n + 1 − c n t = 0 maxn-minn+1-cnt=0 maxnminn+1cnt=0,其中 m a x n maxn maxn代表区间最大值, m i n n minn minn代表区间最小值, c n t cnt cnt代表区间不同数的个数。通过观察我们可以发现,假如一个区间是不合法的那么这个区间的 m a x n − m i n n + 1 − c n t maxn-minn+1-cnt maxnminn+1cnt的值一定是大于 0 0 0的,因为我们可以假设区间最值没有发生变化,那么让区间不合法的原因就是区间不同数的数量比较少,那么我们最后的值一定是大于0的,综上我们可以得到一个式子 m a x n − m i n n − c n ≥ − 1 maxn-minn-cn\geq-1 maxnminncn1
  3. 我们考虑用线段树维护上面的式子,因为线段树可以比较容易的解决区间最值计数问题。
  4. 现在我们考虑让我们枚举右端点的时候如何去更新答案,当我们的右端点移动到R时,我们要考虑某些区间的最值会如何变化,这里可以用单调栈去很轻松的维护出来,当某个数出栈的时候我们消除它的贡献,当一个数进栈的时候我们去加上它的贡献。
  5. 关于计算区间不同数的数量这个问题,我们可以用 m a p map map去记录这个数上次出现的位置,同样也可以轻松的维护。

如果不是很懂的话没关系,可以直接的看下面的代码,中间给了很清楚的注释。

#include<bits/stdc++.h>
#define endl "\n"
#define x first
#define y second
#define Endl endl
typedef long long ll;
const int INF=0x7fffffff;
const double PI=3.14159265359;
using namespace std;
const int N=1e5+10;
map<int,int>col;
int a[N];
int idx=0;
int stk1[N],stk2[N],idx1,idx2;//stk1维护max  stk2维护min 
struct point{//线段树维护区间 maxn-minn+1-cnt的最小值以及最小值的数量 
	int l,r;
	int minn;
	int sum;
	int lazy;
}tr[N*4];
void pushdown(int root){
	if(tr[root].lazy!=0){
		tr[root].minn+=tr[root].lazy;
		if(tr[root].l!=tr[root].r){
			int ch=root<<1;
			tr[ch].lazy+=tr[root].lazy;
			tr[ch+1].lazy+=tr[root].lazy;
		}	
		tr[root].lazy=0;
	}
}
void pushup(int root){
	int ch=root<<1;
	pushdown(ch),pushdown(ch+1);
	tr[root].minn=min(tr[ch].minn,tr[ch+1].minn);
	tr[root].sum=0;
	if(tr[root].minn==tr[ch].minn) tr[root].sum+=tr[ch].sum;
	if(tr[root].minn==tr[ch+1].minn) tr[root].sum+=tr[ch+1].sum;
}
void build(int root,int l,int r){
	tr[root].l=l,tr[root].r=r;
	tr[root].sum=1;
	tr[root].minn=0;
	tr[root].lazy=0;
	if(l!=r){
		int ch=root<<1;
		int mid=(l+r)>>1;
		build(ch,l,mid);
		build(ch+1,mid+1,r);
	}
}
void change(int root,int l,int r,int x){
	pushdown(root);
	if(tr[root].l==l&&tr[root].r==r){
		tr[root].lazy+=x;
		return;
	}
	int mid=(tr[root].l+tr[root].r)>>1;
	int ch=root<<1;
	if(r<=mid) change(ch,l,r,x);
	else if(l>mid) change(ch+1,l,r,x);
	else change(ch,l,mid,x),change(ch+1,mid+1,r,x);
	pushup(root);
}
void solve(){
	ll ans=0;
	idx1=0,idx2=0;
	col.clear();
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	cout<<"Case #"<<++idx<<": ";
	build(1,1,n);
	for(int i=1;i<=n;i++){//枚举右端点 
		int l,r,temp;
		//考虑维护最大值的情况 
		while(idx1&&a[i]>a[stk1[idx1]]){//我要消除之前的影响 
			temp=a[stk1[idx1]];//a[stk1[idx1]]作为最大值时的区间范围是[l,r] 
			r=stk1[idx1];
			idx1--;
			l=stk1[idx1]+1;
			change(1,l,r,-temp);
		}
		l=stk1[idx1]+1;
		stk1[++idx1]=i;
		r=i;
		change(1,l,r,a[i]);//加上新加入的那个数的影响 
		//考虑维护最小值的情况
		while(idx2&&a[i]<a[stk2[idx2]]){
			temp=a[stk2[idx2]];
			r=stk2[idx2];
			idx2--;
			l=stk2[idx2]+1;
			change(1,l,r,temp);
		} 
		l=stk2[idx2]+1;
		stk2[++idx2]=i;
		r=i;
		change(1,l,r,-a[i]);
		//考虑维护cnt
		if(col[a[i]]) change(1,col[a[i]]+1,i,-1);
		else change(1,1,i,-1);
		col[a[i]]=i;
		ans+=tr[1].sum;
	}
	cout<<ans<<endl;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int T;
	cin>>T;
	while(T--){
		solve();
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值