【題解】HNOI-2016序列

Problem

collapse_bzoj

Solution

這道題在HNOI2016中還算是好的了……

這題中如若去掉多組詢問的話可以在O(nlogn) 的時間內得解(並查集),但多組詢問必定要優化,發現這種其他結構基本上無法涉足的題目就只能上莫隊了(我也不知道爲啥想到莫隊,可能這就是題感吧)

減去O(nn) 只剩下常數時間可供轉移了,那麼就要想法常數轉移

明顯在一個序列變化時答案的變化值取決於邊界消長節點與序列中每一個節點所組成區間的答案,那麼一定只有預處理才能常數解決

考慮一個數ai 只有在前面第一個比其小的元素後面,後面第一個比其小的元素前面的區間纔有效,那麼預處理出pre[]nxt[] 數組分別表示每個元素前後第一個比其小的元素的位置,明顯可以用單調棧O(n) 解決(不會的看代碼),再根據這兩個數組預處理出一個貢獻前綴和與一個貢獻後綴和,那麼轉移的大體貢獻就可以根據這兩個前/後綴和做差得到

剩下的就只有區間的邊緣部分,邊緣部分只用快速查找邊緣部分中的最小值即可,如果也要用常數時間查詢,蒟蒻就只知道st表了

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rg register
#define cl(x) memset(x,0,sizeof(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define abs(x) ((x)>0?(x):(-(x)))

template <typename _Tp> inline _Tp read(_Tp&x){
    rg 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=100100;
struct Query{int l,r,id;}qu[N];
ll a[N],suml[N],sumr[N],Ans[N];
int st[N][18],Log[N],pre[N],nxt[N],sta[N],top(0);
int n,m;

inline int cmp1(const Query&aa,const Query&bb){return aa.l<bb.l;}
inline int cmp2(const Query&aa,const Query&bb){return aa.r<bb.r;}

inline int query(int l,int r){
    #define get_(x,y) (a[x]<a[y]?(x):(y))
    return get_(st[l][Log[r-l+1]],st[r-(1<<Log[r-l+1])+1][Log[r-l+1]]);
    #undef get_
}

void init();

void pre_pre_suf();

void pre_st();

void pre_pointer();

void div_block();

void Captain_Mo(){
    rg int l(1),r(1),pos;rg ll ans(a[1]);
    for(rg int i=1;i<=m;++i){
        while(r<qu[i].r){++r;pos=query(l,r);ans+=a[pos]*(pos-l+1)+suml[r]-suml[pos];}
        while(qu[i].l<l){--l;pos=query(l,r);ans+=a[pos]*(r-pos+1)+sumr[l]-sumr[pos];}
        while(qu[i].r<r){pos=query(l,r);ans-=a[pos]*(pos-l+1)+suml[r]-suml[pos];--r;}
        while(l<qu[i].l){pos=query(l,r);ans-=a[pos]*(r-pos+1)+sumr[l]-sumr[pos];++l;}
        Ans[qu[i].id]=ans;
    }
    return ;
}

void Print();

int main(){
    init();
    pre_pointer();
    pre_st();
    pre_pre_suf();
    div_block();
    Captain_Mo();
    Print();
    return 0;
}

void pre_pre_suf(){
    for(rg int i=1;i<=n;++i)suml[i]=suml[pre[i]]+(i-pre[i])*a[i];
    for(rg int i=n;i;--i)   sumr[i]=sumr[nxt[i]]+(nxt[i]-i)*a[i];
    return ;
}

void Print(){for(rg int i=1;i<=m;++i)printf("%lld\n",Ans[i]);return ;}

void div_block(){
    sort(qu+1,qu+m+1,cmp1);
    int block=sqrt(1.0*m);
    for(rg int i=1;i<=m;i+=block)
        sort(qu+i,qu+min(i+block,m),cmp2);
    return ;
}

void pre_st(){
    #define get_(x,y) (a[(x)]<a[(y)]?(x):(y))
    Log[0]=-1;
    for(rg int i=1;i<=n;++i) Log[i]=Log[i>>1]+1,st[i][0]=i;

    for(rg int i=n;i;--i)
    for(rg int j=1;i+(1<<j)-1<=n;++j)
        st[i][j]=get_(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    return ;
    #undef get_
}

void pre_pointer(){
    for(rg int i=1;i<=n;++i){
        while(top&&a[sta[top]]>a[i])nxt[sta[top]]=i,--top;
        pre[i]=sta[top];
        sta[++top]=i;
    }
    return ;
}

void init(){
    read(n),read(m);
    for(rg int i=1;i<=n;++i)read(a[i]);
    for(rg int i=1;i<=m;++i)read(qu[i].l),read(qu[i].r),qu[i].id=i;
    return ;
}
發佈了100 篇原創文章 · 獲贊 49 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章