51nod區間的價值

題目描述

我們定義“區間的價值”爲一段區間的最大值*最小值。
一個區間左端點在L,右端點在R,那麼該區間的長度爲(R-L+1)。
現在聰明的傑西想要知道,對於長度爲k的區間,最大價值的區間價值是多少。
當然,由於這個問題過於簡單。
我們肯定得加強一下。
我們想要知道的是,對於長度爲1~n的區間,最大價值的區間價值分別是多少。
數據範圍1<=n<=100000,1<=ai<=10^9,(由於某種不可抗力,ai的值將會是1~10^9內隨機的一個數)

題目特別強調了ai的值是隨機的,這就是在提示我們要考慮期望複雜度這種東西。
設一個數i可以跳到左邊第一個小於它的數j,數j又可以跳到左邊第一個小於它的數k,那麼我們就稱i可以跳到j和k,(性質1)那麼一個數能夠跳到的數的平均期望個數爲log(n)(只向右跳也是一樣)。
證明:
設f[i]表示數i能夠跳到的數的期望個數,設ans[i]表示前i個數能夠跳到的數的期望個數的平均值
因爲數據隨機,所以數i第一步跳到左邊任意一個位置的概率相等,所以f[i]=∑i−1j=1f[j]+1i−1=ans[i−1]+1
而ans[i]=ans[i−1]∗(i−1)+f[i]i=ans[i−1]+1i
所以ans[n]=∑ni=11i
顯然ans[n]<=log(n),得證。
另外,(性質2)對於長度爲k的區間的最大價值一定大於等於長度爲k+1的區間的最大價值(這個證明比較簡單,這裏就不解釋了)。
利用這些性質,可以開始解這題了。
設l[i]表示i左邊第一個小於它的數,r[i]表示i右邊第一個小於它的數。
那麼我們麼,枚舉一個最大值的位置i,那麼還需要枚舉一個最小值的位置j,然而怎樣枚舉j呢?
從i沿着l[i]跳來得到j,根據性質1,那麼顯然j的個數是log(n)的。
在那麼包含i和j的最大區間爲(l[j]+1,r[j]-1)(因爲j是這個區間的最小值,所以r[j]-1>=i)。
可能有點疑惑,我們並不能保證a[i]是這個區間的最大值,但是對答案是沒有錯誤影響的(即不會得到一個比正確答案還要大的答案)的,因此當i枚舉到這個區間的最大值時,a[i]*a[j]>a[現在的i]*a[j],答案會被更新。
每次只需更新最大區間的答案,因爲性質2,我們可以最後在從大長度到小長度更新答案。
代碼

代碼

#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define ll long long 
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=100000+5;
int i,j,n,a[maxn],l[maxn],r[maxn];
ll ans[maxn];
int main(){
    scanf("%d",&n);
    fo(i,1,n) scanf("%d",&a[i]);
    fo(i,1,n) {j=i-1;while (j&&a[j]>=a[i]) j=l[j];l[i]=j;}
    fod(i,n,1) {j=i+1;while (j<=n&&a[j]>=a[i]) j=r[j];r[i]=j;}
    fo(i,1,n) {
        j=i;while (j) {
            int len=r[j]-l[j]-1;
            ans[len]=max(ans[len],(ll)a[i]*a[j]);j=l[j];
        }
        j=r[i];while (j<=n){
            int len=r[j]-l[j]-1;
            ans[len]=max(ans[len],(ll)a[i]*a[j]);j=r[j];
        }
    }
    fod(i,n-1,1) ans[i]=max(ans[i],ans[i+1]);
    fo(i,1,n) printf("%lld\n",ans[i]);
}
發佈了80 篇原創文章 · 獲贊 21 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章