首页 > 其他分享 >P6792 [SNOI2020] 区间和

P6792 [SNOI2020] 区间和

时间:2023-01-03 16:38:19浏览次数:55  
标签:int max void mid P6792 Delta 区间 ql SNOI2020

对于修改,看上去要用 Segment Tree Beats 维护。

查询根据经典套路,维护每个结点的最大前缀和最大后缀。

我们知道 Segment Tree Beats 的思想是仅处理仅会修改最小值的区间,其他的暴力递归,不过增加最小值对答案的影响是什么呢?乍一看最大前缀的区间不会修改,但是仔细考虑后会发现:[3, -1, 2, -1],如果将 -1 全部改为 1,那么最大前缀的右端点会右移, 同理最大后缀的左端点会左移。

观察后会发现,最大前缀右移是因为有些前缀修改前的和较小但其包含了许多的最小值,当最小值增加 \(\Delta\) 时它的和的增量非常大。容易发现存在一个阈值 \(\max\Delta\),当答案小于这个数的时候区间一定不会变动。

假设有一个前缀被我们认为是“可能的未来最大前缀”,设其和为 \(s'\), 包含了最小值 \(cnt'\) 次,而当前和维 \(s\),包含最小值 \(cnt\) 次,那么要使前者转移后者,就需要满足 \(s < s' + (cnt' - cnt) \Delta\), 移项后得到
\(\dfrac{s - s'}{cnt'-cnt} < \Delta\)。

问题是,有可能存在很多的这样区间。考虑交给左右儿子分别处理。如果先令 \(\max\Delta\gets\min\{\max\Delta_l,\max\Delta_r\}\),那么不难发现,若 \(\Delta<\max\Delta\),那么左右儿子的最大前缀区间都不会改变。此时的“可能未来最大前缀”就只有一个,就是当实际最大前缀是左儿子的最大前缀时,右儿子最大前缀加上左儿子的全部区间。

由于我们只对区间取 \(\max\),固最大前缀等只会不断右移,所以复杂度是正确的,每个节点只会额外的重构操作一次。

还有一个小细节需要注意:如果有多个最大前缀区间可以选择,选择最长的。如果选择最短的可能会导致不断的右移导致复杂度错误。

经过势能分析,就能得出时间复杂度为 \(O(n\log^2n)\)。

#include <iostream>
#include <cstdio>
#define int long long
static const int N = 100005, inf = 1e9;
template<typename T>
void read(T &x) {
    x = 0; bool f = false; int ch = getchar();
    for (; !isdigit(ch); ch = getchar()) f ^= (ch == '-');
    for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
    x = f ? ~x + 1 : x;
}
template<typename T, typename ...Args>
void read(T &x, Args &...args) {read(x); read(args...);}
void cmin(int &x, int y) {if (y < x) x = y;}
void cmax(int &x, int y) {if (y > x) x = y;}
int n, q, a[N];
struct SegmentTree {
    #define lson (x << 1)
    #define rson ((x << 1) | 1)
	struct que {
        int val, cnt;
        que(int x = -inf, int v = 0): val(x), cnt(v) {}
        friend bool operator < (que x, que y) {return x.val < y.val;}
        friend que operator + (que x, que y) {return que(x.val + y.val, x.cnt + y.cnt);}
        void add(int x) {val += x * cnt;}
	};
    struct TreeNode {
        int min, minimin, max, mintime;
        que sum, pre, suf, len;
        void clear() {
            min = minimin = mintime = inf;
            max = -inf;
            sum = pre = suf = len = que(0, 0);
        }
        void set(int x) {
            min = max = x;
            minimin = mintime = inf;
            sum = pre = suf = len = que(x, 1);
        }
        void upmin(int x) {
            if (min == x) return;
            min = x;
            sum.cnt = pre.cnt = suf.cnt = len.cnt = 0;
        }
    } t[N << 2];
    int calc(que pre, que suf) {
        if (suf.cnt <= pre.cnt) return inf;
        return (pre.val - suf.val) / (suf.cnt - pre.cnt);
    }
    void pushup(TreeNode &x, TreeNode ls, TreeNode rs) {
        x.min = std::min(ls.min, rs.min), x.minimin = std::min(ls.minimin, rs.minimin);
        if (ls.min < rs.min) cmin(x.minimin, rs.min);
        if (rs.min < ls.min) cmin(x.minimin, ls.min);
        ls.upmin(x.min), rs.upmin(x.min);
        x.sum = ls.sum + rs.sum, x.len = std::max(std::max(ls.len, rs.len), ls.suf + rs.pre);
        x.pre = std::max(ls.pre, ls.sum + rs.pre), x.suf = std::max(rs.suf, rs.sum + ls.suf);
        x.mintime = std::min(ls.mintime, rs.mintime);
        cmin(x.mintime, x.min + std::min(calc(x.pre, ls.sum + rs.pre), calc(x.suf, rs.sum + ls.suf)));
        cmin(x.mintime, x.min + std::min(std::max(calc(x.len, ls.len), calc(x.len, rs.len)), calc(x.len, ls.suf + rs.pre)));
    }
    void pushdown(int x, int l, int r) {
        int mid = (l + r) >> 1;
        dfs(lson, l, mid, t[x].max);
        dfs(rson, mid + 1, r, t[x].max);
        t[x].max = -inf;
    }
    void dfs(int x, int l, int r, int v) {
        if (t[x].min >= v) return;
        if (t[x].minimin > v && t[x].mintime > v) {
            int del = v - t[x].min;
            t[x].sum.add(del); t[x].len.add(del);
            t[x].pre.add(del); t[x].suf.add(del);
            t[x].min = v; t[x].max = v;
            return void();
        }
        t[x].max = v;
        pushdown(x, l, r);
        pushup(t[x], t[lson], t[rson]);
    }
    void build(int x, int l, int r) {
        t[x].clear();
        if (l == r) return t[x].set(a[l]);
        int mid = (l + r) >> 1;
        build(lson, l, mid); build(rson, mid + 1, r);
        pushup(t[x], t[lson], t[rson]);
    }
    void update(int x, int l, int r, int ql, int qr, int v) {
        if (ql <= l && r <= qr) return dfs(x, l, r, v);
        int mid = (l + r) >> 1; pushdown(x, l, r);
        if (ql <= mid) update(lson, l, mid, ql, qr, v);
        if (qr > mid)  update(rson, mid + 1, r, ql, qr, v);
        pushup(t[x], t[lson], t[rson]);
    }
    void query(int x, int l, int r, int ql, int qr, TreeNode &ans) {
        if (ql <= l && r <= qr) return pushup(ans, ans, t[x]);
        int mid = (l + r) >> 1; pushdown(x, l, r);
        if (ql <= mid) query(lson, l, mid, ql, qr, ans);
        if (qr > mid)  query(rson, mid + 1, r, ql, qr, ans);
    }
} tr;
signed main() {
    read(n, q);
    for (int i = 1; i <= n; ++i) read(a[i]);
    tr.build(1, 1, n);
    while (q--) {
        static int op, l, r, x;
        read(op, l, r);
        if (op == 0) read(x), tr.update(1, 1, n, l, r, x);
        if (op == 1) {
            SegmentTree::TreeNode ans = SegmentTree::TreeNode();
            ans.clear();
            tr.query(1, 1, n, l, r, ans);
            printf("%lld\n", ans.len.val);
        }
    }
    return 0;
}

标签:int,max,void,mid,P6792,Delta,区间,ql,SNOI2020
From: https://www.cnblogs.com/Maraschino/p/17022588.html

相关文章

  • 区间dp
    A题目链接核心思路:这很明显是一道区间dp板子题集合定义:\(f[i][j]表示的是将序列的第i个数合并到第j个数\)全部合并所能得到的最大值。集合划分:和石子合并的区间划分是......
  • 浅谈区间MEX问题
    区间MEX问题MEX是什么​ MEX:指一个序列中最小没有出现过的自然数。​ 例如\(mex\left\{1,2,0,3\right\}=4\),\(mex\left\{5,1,2,3\right\}=0\),\(mex\left\{0......
  • 区间dp
    例题:石子合并设有\(N\)堆石子排成一排,其编号为\(1,2,3,…,N\)。每堆石子有一定的质量,可以用一个整数来描述,现在要将这\(N\)堆石子合并成为一堆。每次只能合并相邻的两......
  • 按编号区间拆分成行
    问题:编号区间为001-010,拆分成10行,如图所示。let源=Excel.CurrentWorkbook(){[Name="表1"]}[Content],最大编号=Table.AddColumn(源,"最大编号",eachif......
  • AcWing362. 区间
    题目描述给定\(n\)个区间\([a_i,b_i]\)和\(n\)个整数\(c_i\)。你需要构造一个整数集合\(Z\),使得\(\foralli\in[1,n]\),\(Z\)中满足\(a_i\lex\leb_i\)......
  • cf-1767C-Count Binary Strings(区间dp)
    题面https://codeforces.com/problemset/problem/1767/C下面展示带注释的ac代码在代码里解释思路Ac代码#include<bits/stdc++.h>#defineioios::sync_with_stdio(f......
  • 重叠区间、合并区间、插入区间问题详解
    判断区间是否重叠题目链接252.合并区间给定一个会议时间安排的数组intervals,每个会议时间都会包括开始和结束的时间intervals[i]=[starti,endi],请你判断一个人......
  • 获取时间区间数据
    原数据:结果:vararr=[{"label":"00:00-00:15","count":3},{"label":"00:15-00:30",......
  • AcWing246. 区间最大公约数
    题目描述给定一个长度为\(N\)的数列\(A\),以及\(M\)条指令,每条指令可能是以下两种之一:Clrd,表示把\(A[l],A[l+1],…,A[r]\)都加上\(d\)。Qlr,表示询问\(A[l......
  • HDU4553 线段树维护最长连续区间
    //题意:(略了)//思路:这里很明显是要维护区间最大连续子段,按照以下优先级查找//A1.左边区间的连续子段是否满足//A2.左右两个区间中间合并起来的子段是否满足......