1 条题解

  • 0
    @ 2026-4-25 13:51:34

    题解区写的都是什么神秘东西,来个真正直观自然的思路。

    先考虑单次询问怎么做,尝试编一个贪心策略出来。将前缀和数组用折线图画出来,则前缀和非负相当于要求整条折线时刻非负,后缀和非负相当于要求折线最后一个位置是全局最大值。显然数组里的 11 我们都会选,要做的事情就是删去最少的 1-1 使得折线满足要求,而删去一个位置的 1-1 对折线图的影响就是后缀向上平移一个单位。

    考虑先把折线调成非负的,策略显然就是每当折线变成负数的时候就把当前最靠后的 1-1 删掉就好,删最靠后的目的是使被加的数尽可能少来减少下一部分的操作次数。此时我们以最少步数得到了一条非负的新折线,再考虑把新折线的最后一个位置调成最大值,只需要求出当前的最大值 mxmx 和当前结尾的值 eded,在中间随便选 mxedmx-ed1-1 删去即可。

    此时复杂度容易做到 O(nq)O(nq),考虑如何快速维护。首先考虑第一部分将折线调成非负的次数,你会发现这里删去的个数正好就是区间内前缀和数组最小值的绝对值(前提是最小值为负,否则不用调),道理很简单,折线最小值前面至少要删去最小值的绝对值个 1-1,而删去后折线后面的位置显然都会被平移成非负的。所以这个是好维护的,把前缀和数组拍到线段树上,支持区间加查区间最小值即可。

    再考虑第二部分,最后结尾的值 eded 肯定是好求的,关键在于新的 mxmx 怎么求。同样借助折线图考虑,发现对于任意一个位置,它前面被删去的 1-1 个数其实就是这个位置前缀和数组上前缀 min\min 的相反数(前提是前缀 min\min 为负数,否则不会有 1-1 被删),也就是 sumimin(0,minj=li1sumj)sum_i-\min(0,\min_{j=l}^{i-1}sum_j) 的最大值。发现我们可以直接放宽成求 maxj<isumisumj\max_{j<i} sum_i-sum_j,这个信息同样可以将前缀和数组拍到线段树上直接维护。

    综上本题复杂度 O(qlogn)O(q\log n),个人代码实现比较唐所以多了不少小特判。

    #include<bits/stdc++.h>
    //#define int long long
    //#pragma GCC optimize(2, 3, "Ofast", "inline")
    #define For(i, a, b) for(int i = (a); i <= (b); i++)
    #define Rof(i, a, b) for(int i = (a); i >= (b); i--)
    using namespace std;
    template<typename T> void cmax(T &x, T y){x = x < y ? y : x;}
    template<typename T> void cmin(T &x, T y){x = x > y ? y : x;}
    const int N = 5e5 + 5, inf = 1e9;
    int n, q, a[N], sum[N];
    struct node{
    	int mx, mn, res;
    }tr[N << 2];
    int tag[N << 2];
    node operator+(const node &a, const node &b){
    	node res;
    	res.mx = max(a.mx, b.mx); res.mn = min(a.mn, b.mn);
    	res.res = max({a.res, b.res, b.mx - a.mn});
    	return res;
    }
    #define ls now << 1
    #define rs now << 1 | 1
    inline void pushup(int now){tr[now] = tr[ls] + tr[rs];}
    inline void pusht(int now, int v){tr[now].mx += v; tr[now].mn += v; tag[now] += v;}
    void pushdown(int now){
    	if(!tag[now]) return;
    	pusht(ls, tag[now]); pusht(rs, tag[now]);
    	tag[now] = 0;
    }
    void build(int l, int r, int now){
    	if(l == r) return tr[now] = {sum[l], sum[l], -inf}, void();
    	int mid = (l + r) >> 1;
    	build(l, mid, ls); build(mid + 1, r, rs);
    	pushup(now);
    }
    void modify(int x, int y, int v, int l, int r, int now){
    	if(x <= l && r <= y) return pusht(now, v);
    	int mid = (l + r) >> 1; pushdown(now);
    	if(x <= mid) modify(x, y, v, l, mid, ls);
    	if(y > mid) modify(x, y, v, mid + 1, r, rs);
    	pushup(now);
    }
    int qval(int p, int l, int r, int now){
    	if(!p) return 0;
    	if(l == r) return tr[now].mx;
    	int mid = (l + r) >> 1; pushdown(now);
    	if(p <= mid) return qval(p, l, mid, ls);
    	return qval(p, mid + 1, r, rs);
    }
    node query(int x, int y, int l, int r, int now){
    	if(x <= l && r <= y) return tr[now];
    	int mid = (l + r) >> 1; pushdown(now);
    	if(y <= mid) return query(x, y, l, mid, ls);
    	if(x > mid) return query(x, y, mid + 1, r, rs);
    	return query(x, y, l, mid, ls) + query(x, y, mid + 1, r, rs);
    }
    #undef ls
    #undef rs
    void Solve(){
    	cin >> n >> q;
    	For(i, 1, n) cin >> a[i], sum[i] = sum[i - 1] + a[i];
    	build(1, n, 1);
    	while(q--){
    		int op, l, r; cin >> op;
    		if(op == 1){
    			cin >> l; int v = -2 * a[l];
    			modify(l, n, v, 1, n, 1); a[l] = -a[l];
    		}
    		else{
    			cin >> l >> r;
    			node res = query(l, r, 1, n, 1);
    			int ans = r - l + 1, cnt1 = 0, cnt2 = 0, d = qval(l - 1, 1, n, 1);
    			res.mx -= d; res.mn -= d;
    			if(max(res.mx, res.res) < 0){cout << 0 << '\n'; continue;}
    			if(res.mn < 0) cnt1 = -res.mn;
    			cnt2 = max(res.res, res.mx) - (qval(r, 1, n, 1) - d + cnt1);
    			cout << ans - cnt1 - cnt2<< '\n';
    		}
    	}
    }
    signed main(){
    	cin.tie(0)->sync_with_stdio(0);
    	int T = 1; //cin >> T;
    	while(T--) Solve();
    	return 0;
    }
    
    • 1

    「UOI 2025 Stage 4 Day1」简单子序列

    信息

    ID
    10192
    时间
    1500ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者