題解 UVA1619 【UVA1619 感覺不錯 Feel Good】
阿新 • • 發佈:2021-07-29
UVA1619 感覺不錯 Feel Good
題目大意:
給出正整數n和一個\((1 <= n <= 100 000)\)長度的數列,要求找出一個子區間,使這個子區間的數字和乘上子區間中的最小值最大。輸出這個最大值與區間的兩個端點。
solution:
很暴力的去想,我們可以列舉區間\([l,r]\),遍歷這個區間找到最小值,同時算出區間和。但很明顯時間複雜度爆炸\(O(N^3)\)
我們再審一遍題:
使這個子區間的數字和乘上子區間中的最小值最大
既然列舉區間行不通,不妨試著列舉最小值:對於每一個數列中的數字 \(a_i\) ,找到當它作為數列最小值時最長的序列,得到序列的端點 \(l,r\)
接下來是細節的處理:
我們想要找到當\(a[i]\)作為最小值的最長序列,就要找到\(a[i]\)左邊、右邊最後一個比它大的數,或者說找到第一個比它小的數,左右分別為\(a[l]\)、\(a[r]\),我們想要得到的左端點、右端點就為:\(l+1\)、\(r-1\)
現在我們請出主角:單調棧
在這不多做贅述,不會的自己看看吧
具體操作見程式碼:
//找右端第一個比a[i]小的數,把右端點賦成i-1 for(int i=1;i<=n;i++) { while(top&&a[i]<a[st[top]]) you[st[top--]]="i-1;" st[++top]="i;" }="" while(top)="" 找左端第一個比a[i]小的數,把左端點賦成i+1="" for(int="" i="n;i">=1;i--) { while(top&&a[i]<a[st[top]]) zuo[st[top--]]="i+1;" st[++top]="i;" }="" while(top)="" ```="" 再處理下字首和="" ```cpp="" for(int="" i="1;i<=n;i++)" {="" scanf("%lld",&a[i]);="" p[i]="p[i-1]+a[i];" ~~完結撒……~~**此題還有很多坑點**="" uva是多組輸入="" 我們需要="" while(scanf("%d",&n)!="EOF)" 此題**沒有**spj,需要在最大值相同時,使區間儘量**短**,於是出現了下面的判斷:="" long="" now;="" 不開long="" long見祖宗="" now="a[i]*(p[you[i]]-p[zuo[i]-1]);//當前乘積" if(now="">ans||(now==ans&&you[i]-zuo[i]</a[st[top]])></a[st[top]])>