P8476 「GLR-R3」惊蛰 题解

笔记列表

天依题
\天依/\天依/\天依/


笔记列表


首先可以想到一个朴素 \(dp\),设 \(g_{i,j}\) 表示第 \(i\) 位 \(b_i=j\) 时的最小代价

转移 \(g_{i,j}=min_{k\ge j}^{} g_{i-1,k} + f(j,a_i)\)

优化它,先给 \(g\) 上一个后缀取最小值,即 \(h_{i,j}=min(g_{i,j},h_{i,j+1})\)

转移变成了 \(g_{i,j} = h_{i-1,j} + f(j,a_i)\)


然后发现 \(g\) 是分段单调不降的
因为首先后缀最小值是单调不降的 \(h_{i,j}\leq h_{i,j+1} \leq h_{i,j+2} \leq ...\)

然后在这个单调不降的东西的基础上加了一个 \(f(j,a_i)\)

  • 当 \(j<a_i\) 时,加的是一个常数, 所以 \([1,a_i]\) 是单调不降的

  • 当 \(j \ge a_i\) 时, \(j\) 越大加的越多,然后两个单调不降和起来还是单调不降,所与 \([a_i+1,len]\) 还是单调不降的

重要结论:每一个 \(g_i\) 在进行求后缀最小值这一步之前是分段单调不降的


然后这个时候我们就线段数实现以下两个功能

  • 每一轮快速的算出所有 \(g_{i,j}\)
  • 每一轮快速的对 \(g\) 做后缀取最小值运算 \(g_{i,j}=min(g_{i,j},g_{i,j+1})\)

第一个功能

  • 首先需要对 \(a_i\) 离散化, 因为你的 \(b_i\) 如果填写的如果时没有出现过的 \(a\) 值肯定没有必要
  • 枚举当前的 \(i\)
  • 让线段数的每一个位置的值表示 \(g_{i,j}\)
  • 现在线段数上是上一轮求的 \(g_{i-1,j}\)
  • 这里上一轮已经对 \(g_{i-1}\)做完了求后缀最小值的操作,所以 \(g_{i-1}\) 整体是单调不降的
  • 要求出 \(g_{i,j}\)
  • 根据转移公式给 \([1,a_i-1]\) 区间加 \(C\);
  • 给\([a_i+1,len] 加上 b_i-i\)
  • 这里 \(b_i\) 表示离散化之前的原值
  • 第一个只需要支持区间加法就可以满足
  • 第二个可以让线段数记录一个懒标记,表示区间内每一个位置加了多少个 \(b_i\)

第二个功能

  • 现在的 \(g\) 做完了转移,我们要对他求后缀最小值
  • 根据结论现在的 \(g\) 应该是分段单调不降的
  • 所以会在求后缀最小值更新的情况一定是后半段\([a_i+1, len]\)的最小值 \(g_{i,a_i}\) 更新了 前半段 \([1,a_i]\) 的一个后缀
  • 那么我们找到 \([1,len]\) 中 从左到右 第一个大于等于 \(g_{i,a_i}\) 的位置\(pos\),然后区间修改\([pos+1,i-1]\) 赋值成 \(g_{i,a_i}\)
  • 这样就实现了后缀最小值更新的功能

最后一个位置填什么都可以,取最小值所以答案就是

\[min_j^{}\;g_{n,j} \]

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define fore(i,a,b) for( int i=(a); i<=(b); ++i)
#define repe(i,a,b) for( int i=(a); i>=(b); --i)
using namespace std;
const int N = 1e6 + 10;
int n, C;
int a[N];
int b[N];
int lsh[N], len;

inline int ls(int p) {
    return (p << 1);
}
inline int rs(int p) {
    return ((p << 1) | 1);
}

struct Segtr {
    int t[N << 2];
    int add[N << 2], mul[N << 2], tag[N << 2];
    inline void pushup(int p) {
        t[p] = max(t[ls(p)], t[rs(p)]);
    }
    void build(int p,int l,int r) {
        add[p] = 0; mul[p] = 0;
        tag[p] = -1;
        if(l == r) {
            return;
        }
        int mid = (l + r) >> 1;
        build(ls(p), l, mid);
        build(rs(p), mid + 1, r);
        pushup(p);
    }
    void updateson(int p,int r,int Add,int Mul,int Tag) {
        if(Tag != -1) {
            t[p] = Tag;
            tag[p] = Tag;
            add[p] = 0;
            mul[p] = 0;
            // return;
        }
        mul[p] += Mul;
        add[p] += Add;
        t[p] = t[p] + Add + Mul * b[r];
    }
    void pushdown(int p,int l,int r) {
        if(add[p] == 0 && mul[p] == 0 && tag[p] == -1) return;
        int mid = (l + r) >> 1;
        updateson(ls(p), mid, add[p], mul[p], tag[p]);
        updateson(rs(p), r, add[p], mul[p], tag[p]);
        add[p] = mul[p] = 0;
        tag[p] = -1;
    }
    void update(int p,int l,int r,int L,int R,int Add,int Mul,int Tag) {
        if(L > R) return;
        if(L <= l && r <= R) {
            updateson(p, r, Add, Mul, Tag);
            return;
        }
        pushdown(p, l, r);
        int mid = (l + r) >> 1;
        if(L <= mid) update(ls(p), l, mid, L, R, Add, Mul, Tag);
        if(R >= mid + 1) update(rs(p), mid + 1, r, L, R, Add, Mul, Tag);
        pushup(p);
    }
    int querypos(int p,int l,int r,int pos) {
        if(l == r) {
            return t[p];
        }
        pushdown(p, l, r);
        int mid = (l + r) >> 1;
        if(pos <= mid) return querypos(ls(p), l, mid, pos);
        return querypos(rs(p), mid + 1, r, pos);
    }
    int find(int p, int l, int r, int pos, int val) {
        if (r <= pos && t[p] < val) return -1;
        if (l == r) return l;
        int mid = (l + r) >> 1;
        pushdown(p, l, r);
        int res = find(ls(p), l, mid, pos, val); 
        if (res != -1) return res;
        if (mid < pos) return find(rs(p), mid + 1, r, pos, val); 
        return -1;
    }
} T;

signed main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	// freopen(".in","r",stdin);
	// freopen(".out","w",stdout);
	cin >> n >> C;
    fore(i, 1, n) {
        cin >> a[i];
        lsh[++ len] = a[i];
    }
    sort(lsh + 1, lsh + len + 1);
    len = unique(lsh + 1, lsh + len + 1) - lsh - 1;
    fore(i, 1, n) {
        int x = lower_bound(lsh + 1, lsh + len + 1, a[i]) - lsh;
        b[x] = a[i];
        a[i] = x;
    }
    T.build(1, 1, len);
    fore(i, 1, n) {
        T.update(1, 1, len, 1, a[i] - 1, C, 0, -1);
        T.update(1, 1, len, a[i] + 1, len, -b[a[i]], 1, -1);
        int val = T.querypos(1, 1, len, a[i]);
        int pos = T.find(1, 1, len, a[i] - 1, val);
        if(pos == -1) continue;
        T.update(1, 1, len, pos, a[i] - 1, 0, 0, val);
    }
    int ans = 1e18;
    fore(i, 1, len) ans = min(ans, T.querypos(1, 1, len, i));
    cout << ans << '\n';
	return 0;
}

posted @ 2026-09-24 17:05  wmq2012  阅读(18)  评论(0)    收藏  举报