[题解]CF1988E Range Minimum Sum

思路

如果没有删除操作,就是一个典中典。

直接枚举最小值 ai,可以轻松 Θ(n) 找到其所能管辖到的最大区间 [Li,Ri]。形式化地说,找到一个最小的 Li 和一个最大的 Ri,使得 minLixRi{ax}=ai

那么它对答案的贡献是 ai×(iLi+1)×(Rii+1)

加入删除操作,也是差不多的做法,依旧是枚举最小值。可以按照删除元素的位置 t 进行如下分讨:

  1. t[1,Li1)t(Ri+1,n],显然对答案无影响,贡献为 ai×(iLi+1)×(Rii+1)

  2. t[Li,i),在 i 左边的选择会少一种,贡献为 ai×(iLi)×(Rii+1)

  3. t(i,Ri],在 i 右边的选择会少一种,贡献为 ai×(iLi+1)×(Rii)

  4. t=Li1t=Ri+1,比较特殊的一种情况,当删除这个位置后,ai 对应新的区间范围可能变化,需要重新求解。可以直接利用 ST 表二分求解。

所有贡献都是连续的,直接差分计算即可。

Code

#include <bits/stdc++.h>
#define re register
#define fst first
#define snd second
#define ll long long

using namespace std;

typedef pair<int,int> pii;
const int N = 5e5 + 10,M = 24,inf = 1e9 + 10;
int n;
int arr[N],L[N],R[N],lg[N];
ll c[N];

inline int read(){
    int r = 0,w = 1;
    char c = getchar();
    while (c < '0' || c > '9'){
        if (c == '-') w = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9'){
        r = (r << 3) + (r << 1) + (c ^ 48);
        c = getchar();
    }
    return r * w;
}

struct ST{
    #define pot(x) (1 << x)

    pii dp[N][M];

    inline pii Add(pii a,pii b){
        if (a.fst == b.fst) return {a.fst,min(a.snd,b.snd)};
        else if (a.fst < b.fst) return {a.fst,min(a.snd,b.fst)};
        else return {b.fst,min(a.fst,b.snd)};
    }

    inline void build(){
        for (re int i = 2;i <= n;i++) lg[i] = lg[i >> 1] + 1;
        for (re int i = 1;i <= n;i++) dp[i][0] = {arr[i],inf};
        for (re int j = 1;j <= lg[n];j++){
            for (re int i = 1;i + pot(j) - 1 <= n;i++) dp[i][j] = Add(dp[i][j - 1],dp[i + pot(j - 1)][j - 1]);
        }
    }

    inline pii query(int l,int r){
        int len = r - l + 1;
        return Add(dp[l][lg[len]],dp[r - pot(lg[len]) + 1][lg[len]]);
    }

    #undef pot
}st;

inline void add(int l,int r,ll k){
    if (l > r) return;
    c[l] += k; c[r + 1] -= k;
}

inline void solve(){
    n = read();
    fill(c + 1,c + n + 5,0);
    for (re int i = 1;i <= n;i++) arr[i] = read();
    st.build();
    for (re int i = 1;i <= n;i++) L[i] = R[i] = i;
    for (re int i = 1;i <= n;i++){
        while (L[i] > 1 && arr[L[i] - 1] > arr[i]) L[i] = L[L[i] - 1];
    }
    for (re int i = n;i;i--){
        while (R[i] < n && arr[R[i] + 1] > arr[i]) R[i] = R[R[i] + 1];
    }
    for (re int i = 1;i <= n;i++){
        int lnum = i - L[i] + 1,rnum = R[i] - i + 1;
        add(1,L[i] - 2,1ll * lnum * rnum * arr[i]);
        add(L[i],i - 1,1ll * (lnum - 1) * rnum * arr[i]);
        add(i + 1,R[i],1ll * lnum * (rnum - 1) * arr[i]);
        add(R[i] + 2,n,1ll * lnum * rnum * arr[i]);
        if (L[i] > 1){
            int l = 1,r = L[i] - 1;
            while (l < r){
                int mid = l + r >> 1;
                if (st.query(mid,i).snd == arr[i]) r = mid;
                else l = mid + 1;
            }
            int Lnum = i - l;
            add(L[i] - 1,L[i] - 1,1ll * Lnum * rnum * arr[i]);
        }
        if (R[i] < n){
            int l = R[i] + 1,r = n;
            while (l < r){
                int mid = l + r + 1 >> 1;
                if (st.query(i,mid).snd == arr[i]) l = mid;
                else r = mid - 1;
            }
            int Rnum = l - i;
            add(R[i] + 1,R[i] + 1,1ll * lnum * Rnum * arr[i]);
        }
    }
    for (re int i = 1;i <= n;i++) printf("%lld ",c[i] += c[i - 1]);
    puts("");
}

signed main(){
    int T; T = read();
    while (T--) solve();
    return 0;
}

作者:WaterSun

出处:https://www.cnblogs.com/WaterSun/p/18321473

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   WBIKPS  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示