「ABC247E」Max Min

蒟蒻还是第一次切掉 \(E\) 题。

题目简介

给定一个长度为 \(N\) 的序列 \(A\) 和两个数 \(X\) , \(Y\),求有多少个区间 \((a,b)\) 满足 \(\underset{i\in(a,b)}{\max}\{A_i\}=X,\underset{i\in (a,b)}{\min}\{A_i\}=Y\)

分析

有区间,有最值,想到以下两大类做法:

  • 枚举左右端点 \(l,r\),判断 \((l,r)\) 是否合法。使用暴力打擂台 \(\mbox O(n^2)\)\(ST\) 表为 \(\mbox O(n^2)\),线段树为 \(\mbox O(n^2log\ n)\)
  • 枚举总长度 \(len\),同时枚举左右端点 \(l,r\),类似于单调队列一样判断,\(\mbox O(n^2)\)

想一想这两种办法的劣处在哪里。

可以发现,在区间长度变化之时,两种做法均不能很好地利用先前的信息,由此思考如何改进。

不难发现,如果有一个区间 \([st,ed]\) 满足它的所有值均落在 \([X,Y]\) 内,且其子区间 \([l,r]\) 中同时拥有了 \(X\)\(Y\) 。那么区间 \([l,r],[l,r+1],[l,r+2],\dots,[l,ed]\) 都一定合法,就无需再一一枚举 \(r\) ,直接从 \([l+1,r]\) 开始找就可以了。这样对于满足要求的区间 \([st,ed]\),可以 \(\mbox O(n)\) 时间算出其方案数。

剩下的只需要将区间 \([st,ed]\) 全部预处理出来就可以了。

\(AC\ Code\)

#include<cstdio>
#include<vector>
#include<iostream>
using namespace std;
int read(){
	int x=0;
	char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x;
}
const int Maxn=2e5+5;
int a[Maxn];
int st[Maxn],ed[Maxn];
int main(){
	int n=read(),x=read(),y=read();
	int tot=1;
	st[tot]=1,ed[tot]=0;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		if(a[i]<y||a[i]>x){
			++tot;
			st[tot]=i+1,ed[tot]=i;
			continue;
		}
		++ed[tot];
	}
	long long ans=0;
	for(int i=1;i<=tot;++i){
		long long sum=0;
		int cnt_max=0,cnt_min=0;
		int l=st[i];
		for(int r=st[i];r<=ed[i];++r){
			if(a[r]==x)++cnt_max;
			if(a[r]==y)++cnt_min;
			while(cnt_max&&cnt_min){
				sum+=1ll*(ed[i]-r+1);
				if(a[l]==x)--cnt_max;
				if(a[l]==y)--cnt_min;
				++l;
			}
		}
		ans+=sum;
	}
	printf("%lld\n",ans);
	return 0;
} 

$$-----EOF-----$$

posted @ 2022-04-11 20:01  AlienCollapsar  阅读(89)  评论(0编辑  收藏  举报
// 生成目录索引列表 // ref: http://www.cnblogs.com/wangqiguo/p/4355032.html // modified by: zzq