最大最小(區間問題)
阿新 • • 發佈:2020-08-02
最大最小
題目描述:
牛妹有一個數組array,她想知道里面有多少個區間滿足區間最大值大於等於區間最小值的兩倍。
連結:https://ac.nowcoder.com/acm/contest/6778/C
來源:牛客網
輸入:
給定A陣列 長度n 0<n<=100000;0<A[i]<=1e9;
輸出:
返回滿足條件的區間個數輸入: 2 1 2 輸出 1 解釋:只有區間[1,2]滿足。
解法:
- 首先用一個數組儲存A[i]前面第一個比他小的值的下標。即pr[i]
- 再用一個數組儲存A[i]之後第一個比他小的值的下標。即nt[i]
- 然後用同樣的做法,但是中間需要以2分查詢的方式找到在i前面第一個大於2倍A[i]的下標。
- 最後對每一個pr1[i]=max(pr[i],pr[i]) nt1[i]=min(nt[i],nt1[i]):這一步可以在pr1和nt1生成時候做。
- 最後迴圈n次。ans+=(i-pr[i])*(nt[i]-i)-(i-pr1[i])*(nt1[i]-i)。
解析:
為什麼要求第一個前後最小啦?
由於求區間最大最小。求左右最小也就是該數值能成為最小的作用的區域。也就是pr[i]~nt[i]區間A[i]都是最小。
既然我們求得了一個值在最小作用的區域。第二步那就是在此區域內pr[i]~nt[i]大於A[i]2倍的值。用2分找到。
左邊區間即為1~pr1[i]。右邊即為nt1[i]~n;
由於區間最小區域要和大於兩倍A[i]的區域重疊才能有解。(不重疊你就算是區間最最小,沒有大於2倍也沒有區間滿足)
所以會有第4步。不過做的時候可以直接就求出區間重疊部分。(程式碼也是這麼寫的)
最後計算就是:包含i在內所有的區間減去不滿足條件的區間。比如pr1[i]-i之間就不存在大於A[i]2倍的值。所以直接減去。
程式碼:
class Solution { typedef long long ll; public: /** * * @param array int整型一維陣列 array * @param arrayLen int array陣列長度 * @return long長整型 */ int nt[100005],nt1[100005],pr[100005],pr1[100005],s[100005]; long long MaxMin(int* array, int arrayLen) { // write code here int n = arrayLen,*a=array; int t=0; s[t]=-1; for(int i=0;i<n;i++){ while(t&&a[s[t]]>a[i]) t--; pr[i]=s[t]; s[++t]=i; } s[t=0]=n; for(int i=n-1;~i;i--){ while(t&&a[s[t]]>=a[i]) t--; nt[i]=s[t]; s[++t]=i; } s[t=0]=-1; for(int i=0;i<n;i++){ while(t&&a[s[t]]<=a[i]) t--; s[++t]=i; int l=0,r=t; while(l+1<r){ int mid = (l+r)>>1; if(a[s[mid]]<(a[i]<<1)) r=mid; else l=mid; } pr1[i]=max(pr[i],s[l]); } s[t=0]=n; for(int i=n-1;~i;i--){ while(t&&a[s[t]]<=a[i]) t--; s[++t]=i; int l=0,r=t; while(l+1<r){ int mid = (l+r)>>1; if(a[s[mid]]<(a[i]<<1)) r=mid; else l=mid; } nt1[i]=min(nt[i],s[l]); } ll ans=0,p=1; for(int i=0;i<n;i++) ans+=p*(i-pr[i])*(nt[i]-i)-p*(i-pr1[i])*(nt1[i]-i); return ans; } };