Luogu 3246 序列

Luogu 3246 序列

  • 考慮莫隊,不算特別優秀,但足以經過此題.
  • 用莫隊作,先考慮在當前區間右邊加入一個數對答案的影響,其餘三種狀況同理.
  • 若加入新數的區間爲 \([L,R]\) ,那麼加的貢獻就是 \([L,R],[L+1,R]\dots [R,R]\) 這些區間最小值之和.
  • 用單調棧預處理出每一個數 \(a_i\) 左邊第一個比它小的數的位置 \(sl\) ,那麼它被記做最小值的區間就是 \([sl+1,R],[sl+2,R]\dots[i,R]\) ,被算了 \(i-sl\) 次.那麼就這樣一個個往前面跳,相似於樹的結構.
  • 這個東西顯然能夠在算完 \(sl\) 後馬上求出,算一下前綴和,那麼每次查詢也是 \(O(1)\) 的.
  • 注意到最前面那個元素會跳出去,被算的次數不是 \(i-sl\) ,而是 \(i-L+1\),須要單獨算.按照定義,它顯然是 \([L,R]\) 這個區間內的最小值,用 \(ST\) 表問一下位置,大小就能夠了.

寫這個題又複習了一遍莫隊...詢問排序第一關鍵字是左端點的塊,第二關鍵字是右端點...另外,那四個 \(while\) 移動端點的順序不能亂寫...否則會出現 \(L>R\) 的尷尬狀況..若是樣例沒測出來這題可就爆零了...c++

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read()
{
    int out=0,fh=1;
    char jp=getchar();
    while ((jp>'9'||jp<'0')&&jp!='-')
        jp=getchar();
    if (jp=='-')
        fh=-1,jp=getchar();
    while (jp>='0'&&jp<='9')
        out=out*10+jp-'0',jp=getchar();
    return out*fh;
}
const int MAXN=1e5+10;
struct query
{
    int l,r,bel,id;
    bool operator < (const query &rhs) const
    {
        if(bel!=rhs.bel)
            return bel<rhs.bel;
        if(r!=rhs.r)
            return r<rhs.r;
        return l<rhs.l;
    }
} q[MAXN];
int a[MAXN];
int st[MAXN][18],stp[MAXN][18];
int n,Q;
int sl[MAXN],sr[MAXN];
int stk[MAXN],stkpos[MAXN],tp;
ll suml[MAXN],sumr[MAXN];
ll res,ans[MAXN];
int mi,pos;
inline void st_init()
{
    for(int i=1;i<=n;++i)
        st[i][0]=a[i],stp[i][0]=i;
    for(int j=1;(1<<j)<=n;++j)
        for(int i=1;i+(1<<j)-1<=n;++i)
        {
            if(st[i][j-1]<st[i+(1<<(j-1))][j-1])
                st[i][j]=st[i][j-1],stp[i][j]=stp[i][j-1];
            else
                st[i][j]=st[i+(1<<(j-1))][j-1],stp[i][j]=stp[i+(1<<(j-1))][j-1];
        }
}
void query(int l,int r)
{
    int k=log(r-l+1)/log(2.0);
    if(st[l][k]<st[r-(1<<k)+1][k])
        mi=st[l][k],pos=stp[l][k];
    else
        mi=st[r-(1<<k)+1][k],pos=stp[r-(1<<k)+1][k];
}
void addl(int L,int R)
{
    query(L,R);
    ll delta=1LL*(R-pos+1)*mi;
    delta+=sumr[L]-sumr[pos];
    res+=delta;
}
void reml(int L,int R)
{
    query(L,R);
    ll delta=1LL*(R-pos+1)*mi;
    delta+=sumr[L]-sumr[pos];
    res-=delta;
}
void addr(int L,int R)
{
    query(L,R);
    ll delta=1LL*(pos-L+1)*mi;
    delta+=suml[R]-suml[pos];
    res+=delta;
}
void remr(int L,int R)
{
    query(L,R);
    ll delta=1LL*(pos-L+1)*mi;
    delta+=suml[R]-suml[pos];
    res-=delta;
}
void init()
{
    st_init();
    stk[++tp]=0;
    stkpos[tp]=0;
    for(int i=1; i<=n; ++i)
    {
        while(tp)
        {
            if(a[i]<=stk[tp])
                --tp;
            else
                break;
        }
        sl[i]=stkpos[tp];
        stk[++tp]=a[i];
        stkpos[tp]=i;
        suml[i]=suml[sl[i]]+1LL*(i-sl[i])*a[i];
    }
    tp=0;
    stk[++tp]=0;
    stkpos[tp]=n+1;
    for(int i=n; i>=1; --i)
    {
        while(tp)
        {
            if(a[i]<=stk[tp])
                --tp;
            else
                break;
        }
        sr[i]=stkpos[tp];
        stk[++tp]=a[i];
        stkpos[tp]=i;
        sumr[i]=sumr[sr[i]]+1LL*(sr[i]-i)*a[i];
    }
}
int main()
{
    n=read(),Q=read();
    int Blocksize=sqrt(Q);
    for(int i=1; i<=n; ++i)
        a[i]=read();
    for(int i=1; i<=Q; ++i)
    {
        q[i].l=read();
        q[i].r=read();
        q[i].bel=q[i].l/Blocksize;
        q[i].id=i;
    }
    sort(q+1,q+1+Q);
    init();
    int L=1,R=0;
    for(int i=1; i<=Q; ++i)
    {
        int l=q[i].l,r=q[i].r;
        while(R<r)
            addr(L,++R);
        while(L<l)
            reml(L++,R);
        while(L>l)
            addl(--L,R);
        while(R>r)
            remr(L,R--);
        ans[q[i].id]=res;
    }
    for(int i=1; i<=Q; ++i)
        printf("%lld\n",ans[i]);
    return 0;
}
相關文章
相關標籤/搜索