題解-COCI2015Norma

Problem

SPOJ-NORMA2 & bzoj3745php

題意概要:給定一個正整數序列 \(\{a_i\}\),求c++

\[\sum_{i=1}^n\sum_{j=i}^n(j-i+1)\min(a_i,a_{i+1},\cdots,a_j)\max(a_i,a_{i+1},\cdots a_j)\]git

\(n\leq 5\times 10^5\)算法

Solution

這題正解是一個完美的 \(O(n\log n)\) 分治,但比較麻煩,鑑於這個分治作法已經漫天飛了,因此這裏不講那個算法函數

我在考場上在最後二十分鐘想到了並打出了另外一個分治作法,很是很好寫跑得也很快,最終能夠 AC優化

能夠考慮對於一個序列 \(\{a_i\}\),找到其最大值 \(mx\) 與最小值 \(mi\),有大量區間都是以這兩點爲最值點的,而同時這些區間的左右端點分別都是連續的,因此能夠考慮將這些區間一塊兒計算spa

具體的,若找到的最大值與最小值分別在 \(p_1,p_2\) 取到(不妨設 \(p_1\leq p_2\)),則以這二者爲最值點的區間 \([l,r]\) 知足 \(1\leq l\leq p_1,p_2\leq r\leq n\),這些區間的長度和能夠 \(O(1)\) 算出,也便可以 \(O(1)\) 算出這些區間的貢獻code

進一步的,須要加上其餘不是 同時以這二者爲最值點 的區間貢獻。設統計左右端點都在 \([l,r]\) 內的區間貢獻也即剛剛這一步處理爲函數 \(f(l,r)\),則其餘區間的貢獻即 \(f(l,p_2-1)+f(p_1+1,r)-f(p_1+1,p_2-1)\)(因爲前面兩個式子中重複計算了左右端點都在 \([p_1+1,p_2-1]\) 內的區間貢獻,因此須要第三個函數去減去這部分多餘的貢獻)get

因此如今能夠獲得一個基本的作法(統計 \([l,r]\) 區間):it

  • \(O(1)\) 找到區間最大最小值所在位置 \(p_1,p_2(p_1\leq p_2)\)
  • \(O(1)\) 統計左端點在 \([l,p_1]\)、右端點在 \([p_2,r]\) 的區間的貢獻
  • 分治統計區間 \([l,p_2-1],[p_1+1,r]\),並減去 \([p_1+1,p_2-1]\) 的答案

這個作法慢成龜龜,而後我靈機一動:分治下去的區間不是會繼續使用當前最值點爲最值點嗎?(即 \([l,p_2-1]\) 會使用 \(p_1\) 爲最值點,進而可能再次調用區間 \([p_1+1,p_2-1]\),這裏的統計就冗餘了,若是加個記憶化那麼原來每次分出三個區間就能夠均攤成兩個了……)

而後就加了一下 \(map\) 的記憶化,極限數據只須要 \(0.4s\)

以前證了一波僞的複雜度 \(O(n\log n)\),後來被同校 dalao 精心卡掉了 雖然構造了一個多小時

實際上覆雜度是 \(O(n^2\log n)\) 的,那個 \(\log\) 仍是 \(map\) 的複雜度 ~~沒錯這是個暴力,但很難卡滿,在考試中、spoj和bzoj上都沒能卡掉我♪(^∇^*)~~

實際運行效率很高,未經st表優化的代碼在bzoj上跑到 \(\mathrm{rank6}\),比我寫的正解快一倍,同時代碼也很短很好寫 畢竟是在十分鐘內寫完調完的,只有 \(\mathrm{1.2k}\)

Code

因爲想到這個解法時時間緊迫,沒來得及寫 \(st\) 表作 \(\mathrm{rmq}\) 但仍是過掉了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

template <typename _tp> inline _tp read(_tp&x){
    char c11=getchar(),ob=0;x=0;
    while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
}

const int N=501000,p=1e9,inf=0x3f3f3f3f;
int a[N],n;

map <int,int> mp[N];

inline int getsum(int l,int r){return 1ll*(l+r)*(r-l+1)/2%p;}
inline int qm(int x){while(x<0)x+=p;while(x>=p)x-=p;return x;}

int force(int l,int r){
    int res(0);
    for(int i=l;i<=r;++i){
        int mx=-inf,mi=inf;
        for(int j=i;j<=r;++j){
            mx=max(mx,a[j]);
            mi=min(mi,a[j]);
            res=qm(res+1ll*(j-i+1)*mi%p*mx%p);
        }
    }return res;
}

int solve(int l,int r){
    if(l>r)return 0;
    if(mp[l].find(r)!=mp[l].end())
        return mp[l][r];
    if(r-l<=10)
        return mp[l][r]=force(l,r);
    int mx=-inf,mxd;
    int mi=inf,mid;
    for(int i=l;i<=r;++i){
        if(a[i]>mx)mx=a[i],mxd=i;
        if(a[i]<mi)mi=a[i],mid=i;
    }
    int L=min(mxd,mid),dl=L-l+1;
    int R=max(mxd,mid),dr=r-R+1;
    int dx=R-L-1,res(0);
    if(dl>dr)swap(dl,dr);
    for(int i=1;i<=dl;++i)
        res=qm(res+getsum(i+dx+1,i+dx+dr));
    res=1ll*res*mx%p*mi%p;
    return mp[l][r]=qm(res+qm(solve(l,R-1)+solve(L+1,r))-solve(L+1,R-1));
}

int main(){
    read(n);
    for(int i=1;i<=n;++i)read(a[i]);
    printf("%d\n",solve(1,n));
    return 0;
}
相關文章
相關標籤/搜索