1. 程式人生 > 其它 >「ABC247E」Max Min

「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-----$$