「ABC247E」Max Min
阿新 • • 發佈:2022-04-11
蒟蒻還是第一次切掉 \(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]\)
剩下的只需要將區間 \([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; }