【题解】P3246 [HNOI2016] 序列

P3246 [HNOI2016] 序列

题意

给定长度为 \(n\) 的序列。

\(m\) 次询问,每次询问一个区间 \([l,r]\),请求出:

\[\displaystyle \sum_{i=l}^r\sum_{j=i}^r \min_{k=i}^j a_k \]

\(n,m\le 10^5\)

题解

知识点:分治,线段树,扫描线。

题解里没有人使用分治,所以这里给出一个分治做法。

先考虑单次询问,设询问为 \([l,r]\),则从 \([l,r]\) 开始分治,每次取中点 \(mid\) 并递归 \([l,mid]\)\([mid+1,r]\)

假设当前分治区间为 \([l,r]\),则只计算跨过 \(mid\) 的区间的贡献。

为了统计,需要处理出从 \(mid+1\) 开始的前缀 \(a_i\) 最小值,和从 \(mid\) 开始的后缀 \(a_i\) 最小值。

由于这题可能出现相同的数,对于值相同的两个数,这里断言下标更小的更小,以防止记重或者记漏。

然后分讨最小值出现在 \(mid\) 左边还是右边,用两次双指针即可,看代码应该就看懂了。

这一部分的代码。

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

#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()

#define N 102506
#define int long long

int n,m,a[N],s[N];

inline int sol(int l,int r){
    if(l>r){
        return 0;
    }

    if(l==r){
        return a[l];
    }

    int mid=(l+r)>>1,ans=sol(l,mid)+sol(mid+1,r);

    s[mid]=a[mid],s[mid+1]=a[mid+1];

    per(i,l,mid-1){
        s[i]=min(s[i+1],a[i]);
    }

    rep(i,mid+2,r){
        s[i]=min(s[i-1],a[i]);
    }

    int j=mid+1;
    rep(i,mid+1,r){
        while(j>l&&s[j-1]>s[i]){
            j--;
        }

        ans+=s[i]*(mid-j+1);
    }

    j=mid;
    per(i,l,mid){
        while(j<r&&s[j+1]>=s[i]){
            j++;
        }

        ans+=s[i]*(j-mid);
    }

    return ans;
}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);

    cin>>n>>m;

    rep(i,1,n){
        cin>>a[i];
    }

    rep(i,1,m){
        int l,r;
        cin>>l>>r;

        cout<<sol(l,r)<<"\n";
    }

    return 0;
}

这题不强制在线,说明可以离线。

好想一次分治就把所有询问处理出来,嘻嘻。

考虑离线询问,并建出分治树,将询问“插入”树上,类似线段树的方式,每个询问最多会插入到 \(O(\log n)\) 个区间上。

最后一次分治计算出所有询问答案,具体地,对于 \([l,r]\),每一次分讨的过程中都顺便对询问的左/右端点(视情况)做扫描线,在线段树上动态维护区间贡献。

代码很易懂,这里就不细讲了。

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

#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()

#define N 102506
#define int long long

int n,m,a[N],s[N],ans[N],w[N<<2];
vector<int>e[N<<2],L[N],R[N];

struct node{
    int l,r;
}q[N];

struct segt{
    #define mid ((l+r)>>1)

    int s[N<<2],tg[N<<2];

    inline void un(int k){
        s[k]=s[k*2]+s[k*2+1];
    }

    inline void addt(int k,int l,int r,int d){
        tg[k]+=d;
        s[k]+=(r-l+1)*d;
    }

    inline void pd(int k,int l,int r){
        if(tg[k]){
            addt(k*2,l,mid,tg[k]);
            addt(k*2+1,mid+1,r,tg[k]);

            tg[k]=0;
        }
    }

    inline void build(int k,int l,int r){
        tg[k]=0;
        
        if(l==r){
            s[k]=0;
            return;
        }

        build(k*2,l,mid);
        build(k*2+1,mid+1,r);

        un(k);
    }

    inline void upd(int L,int R,int k,int l,int r,int d){
        if(L<=l&&R>=r){
            addt(k,l,r,d);
            return;
        }

        pd(k,l,r);

        if(L<=mid){
            upd(L,R,k*2,l,mid,d);
        }
        if(R>mid){
            upd(L,R,k*2+1,mid+1,r,d);
        }

        un(k);
    }

    inline int ask(int L,int R,int k,int l,int r){
        if(L<=l&&R>=r){
            return s[k];
        }

        pd(k,l,r);

        int ans=0;

        if(L<=mid){
            ans+=ask(L,R,k*2,l,mid);
        }
        if(R>mid){
            ans+=ask(L,R,k*2+1,mid+1,r);
        }

        return ans;
    }

    #undef mid
}t;

inline void ins(int k,int l,int r,int i){
    if(q[i].l<=l&&r<=q[i].r){
        e[k].pb(i);
        return;
    }

    int mid=(l+r)>>1;

    if(q[i].r<=mid){
        ins(k*2,l,mid,i);
        return;
    }

    if(q[i].l>mid){
        ins(k*2+1,mid+1,r,i);
        return;
    }

    e[k].pb(i);
    ins(k*2,l,mid,i);
    ins(k*2+1,mid+1,r,i);
}

inline void sol(int k,int l,int r){
    if(l>r){
        return;
    }

    if(l==r){
        for(int x:e[k]){
            ans[x]+=a[l];
        }
        w[k]=a[l];
        return;
    }

    int mid=(l+r)>>1;

    sol(k*2,l,mid);
    sol(k*2+1,mid+1,r);
    w[k]=w[k*2]+w[k*2+1];

    rep(i,l,r){
        L[i].clear();
        R[i].clear();
    }
    vector<int>tmp;

    for(int x:e[k]){
        if(q[x].l<=l&&r<=q[x].r){
            tmp.pb(x);
            continue;
        }
        L[max(q[x].l,l)].pb(x);
        R[min(q[x].r,r)].pb(x);
    }

    s[mid]=a[mid],s[mid+1]=a[mid+1];

    per(i,l,mid-1){
        s[i]=min(s[i+1],a[i]);
    }
    rep(i,mid+2,r){
        s[i]=min(s[i-1],a[i]);
    }

    t.build(1,l,r);

    int j=mid+1;
    rep(i,mid+1,r){
        while(j>l&&s[j-1]>s[i]){
            j--;
        }

        if(j<=mid){
            t.upd(j,mid,1,l,r,s[i]);
        }

        for(int x:R[i]){
            ans[x]+=t.ask(q[x].l,mid,1,l,r);
        }

        w[k]+=s[i]*(mid-j+1);
    }

    j=mid;
    per(i,l,mid){
        while(j<r&&s[j+1]>=s[i]){
            j++;
        }

        if(j>=mid+1){
            t.upd(mid+1,j,1,l,r,s[i]);
        }

        for(int x:L[i]){
            ans[x]+=t.ask(mid+1,q[x].r,1,l,r);
        }

        w[k]+=s[i]*(j-mid);
    }

    for(int x:tmp){
        ans[x]+=w[k];
    }
}

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);

    cin>>n>>m;

    rep(i,1,n){
        cin>>a[i];
    }

    rep(i,1,m){
        cin>>q[i].l>>q[i].r;
        ins(1,1,n,i);
    }

    sol(1,1,n);

    rep(i,1,m){
        cout<<ans[i]<<"\n";
    }

    return 0;
}
posted @ 2025-07-17 20:24  Lucyna_Kushinada  阅读(33)  评论(0)    收藏  举报