首页 > 其他分享 >吉司机线段树

吉司机线段树

时间:2022-10-03 21:47:52浏览次数:100  
标签:rs int 线段 mid 司机 la4 fi 最大值

luogu P6242线段树3

蒟蒻的代码
// 线段树3
// 需要支持的操作:
// 修改:区间加,区间取min,区间求和
// 查询:区间最大值,区间历史最大值
// 瓶颈在于区间取min
// 引入区间最大值,次大值,最大值的次数
// 实际上是把区间的数分为了最大值和非最大值
// 线段树需要维护的值有
// 区间和sum,区间最大值fi,区间次大值se, 区间历史最大值ma,区间最大值的次数cnt
// 区间最大值标记la1,区间非最大值标记la2,区间最大值历史标记la3,区间非最大值历史标记la4
#include <bits/stdc++.h>
#define re register
#define ll long long
#define ull unsigned long long
using namespace std;
inline ll read()
{
    ll x = 0, f = 0; char c = getchar();
    while (c < '0') f |= (c == '-'), c = getchar();
    while (c >= '0') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const ll inf = 0x7fffffffff;
const int N = 5e5 + 10;
int n, m, a[N];

#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
struct node
{
    ll fi, se, ma, cnt, sum;
    node(){ fi = se = ma = cnt = sum = 0; }
    node(ll fi, ll se, ll ma, ll cnt, ll sum)
    :fi(fi), se(se), ma(ma), cnt(cnt), sum(sum){}
}T[N << 2];
ll la1[N << 2], la2[N << 2], la3[N << 2], la4[N << 2], len[N << 2];

node operator + (const node & a, const node & b)
{
    node c;
    c.sum = a.sum + b.sum ;
    c.ma = max(a.ma , b.ma );
    c.fi = max(a.fi , b.fi );
    if(a.fi == b.fi )
    {
        c.cnt = a.cnt + b.cnt ;
        c.se = max(a.se , b.se );
    }else if(a.fi > b.fi )
    {
        c.cnt = a.cnt ;
        c.se = max(a.se , b.fi );
    }else 
    {
        c.cnt = b.cnt ;
        c.se = max(a.fi , b.se );
    }
    return c;
}

void build(int k, int l, int r)
{
    len[k] = r - l + 1;
    if(l == r)
    {
        T[k] = node(a[l], -inf, a[l], 1, a[l]);
        return ;
    }
    int mid = (l + r) >> 1;
    build(ls(k), l, mid);
    build(rs(k), mid + 1, r);
    T[k] = T[ls(k)] + T[rs(k)];
}

void update(int k, ll s1, ll s2, ll s3, ll s4)
{
    T[k].sum += T[k].cnt * s1 + (len[k] - T[k].cnt ) * s2;
    T[k].ma = max(T[k].ma , T[k].fi + s3); // 先更新一下历史最大值,用当前最大值加历史最大标记
    T[k].fi += s1;
    if(T[k].se != -inf) T[k].se += s2; // 判断一下有没有次小值
    la3[k] = max(la3[k], la1[k] + s3);
    la4[k] = max(la4[k], la2[k] + s4);
    la1[k] += s1;
    la2[k] += s2;
}

void pushdown(int k)
{
    ll maxx = max(T[ls(k)].fi , T[rs(k)].fi );
    if(T[ls(k)].fi == maxx)
        update(ls(k), la1[k], la2[k], la3[k], la4[k]);
    else
        update(ls(k), la2[k], la2[k], la4[k], la4[k]);
    if(T[rs(k)].fi == maxx)
        update(rs(k), la1[k], la2[k], la3[k], la4[k]);
    else
        update(rs(k), la2[k], la2[k], la4[k], la4[k]);
    // 注意左右儿子,不要写错
    la1[k] = la2[k] = la3[k] = la4[k] = 0;
} 

void add(int k, int l, int r, int L, int R, ll val) 
{ 
    if(L <= l && r <= R) 
    { 
        update(k, val, val, val, val); // 区间加,直接update
        return ; 
    } 
    pushdown(k); 
    int mid = (l + r) >> 1; 
    if(L <= mid) add(ls(k), l, mid, L, R, val); 
    if(R > mid) add(rs(k), mid + 1, r, L, R, val); 
    T[k] = T[ls(k)] + T[rs(k)]; 
} 

void change(int k, int l, int r, int L, int R, ll val) 
{ 
    if(T[k].fi <= val) return ; 
    if(L <= l && r <= R && T[k].se < val) 
    { 
        // 这里直接update就好了
        update(k, val - T[k].fi , 0, val - T[k].fi , 0);
        // 注意你要加什么值,还有,注意赋值的先后顺序,最后赋T[k].fi
        // T[k].sum -= T[k].cnt * (T[k].fi - val); 
        // la1[k] -= (T[k].fi - val), T[k].fi = val;
        return ; 
    } 
    pushdown(k); 
    int mid = (l + r) >> 1; 
    if(L <= mid) change(ls(k), l, mid, L, R, val); 
    if(R > mid) change(rs(k), mid + 1, r, L, R, val); 
    T[k] = T[ls(k)] + T[rs(k)]; 
}

node query(int k, int l, int r, int L, int R)
{
    if(L <= l && r <= R) return T[k];
    pushdown(k);
    int mid = (l + r) >> 1;
    if(R <= mid) return query(ls(k), l, mid, L, R);
    if(L > mid) return query(rs(k), mid + 1, r, L, R);
    else return query(ls(k), l, mid, L, R) + query(rs(k), mid + 1, r, L, R);
}

int main()
{
    n = read(), m = read();
    for (re int i = 1; i <= n; ++i) a[i] = read();
    build(1, 1, n);
    while (m--)
    {
        int opt = read(), l = read(), r = read();
        if(opt == 1) add(1, 1, n, l, r, read());
        if(opt == 2) change(1, 1, n, l, r, read());
        if(opt == 3) printf ("%lld\n", query(1, 1, n, l, r).sum );
        if(opt == 4) printf ("%lld\n", query(1, 1, n, l, r).fi );
        if(opt == 5) printf ("%lld\n", query(1, 1, n, l, r).ma );
    }
    return 0;
}

标签:rs,int,线段,mid,司机,la4,fi,最大值
From: https://www.cnblogs.com/wenqizhi1125/p/16751327.html

相关文章

  • 线段树什么的最讨厌了
    发现如果正着从一颗线段树搜到这一个区间,很难搜。所以考虑从一个区间搜出一颗线段树。对于一个区间\([l,r]\),他的父亲区间只可能是\([2*l-r-2,r],[2*l-r-1,r],[l,2*r-l......
  • 省赛线段树存档
    #include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;intread(){intx;scanf("%d",&x);returnx;}llmod=998244353ll,ans;llinv[100010],fa......
  • 线段树的简单扩展及应用
    线段树的简单扩展及应用参考:线段树的高级用法-Alex_Wei注:这篇文章只开了个头就鸽了,如果真的准备学到点什么的话可以直接点上面这篇文章(前言线段树作为一种有效维护区......
  • PADS应用笔记:Layout隐藏线段方框和叉号
    问题用layout看图纸时方框和叉号太影响观感了,如何隐藏方法方框叉号......
  • 线段树学习笔记(入门)
    目录前言线段树基础2.1定义2.2区间操作和懒标记2.3一些例题1.前言应老师要求,来写一篇关于线段树的学习笔记2.线段树基础2.1定义线段树是一种二叉搜索树,与......
  • 需要对某些区间递归处理的线段树的维护
    这篇总结来源于本蒟蒻打了两道题目发现了这种类型题,却不知道怎么给它起名字......对一些已经看出来对区间进行操作和维护,但pushup操作不太容易想出来的题目来说,我们不妨尝......
  • AcWing 算法提高课 线段树+扫描线法 求矩形之并的面积
    例题:求解多个长方形之并的面积https://www.acwing.com/problem/content/249/蓝色表示长方形,红色表示扫描线如下图所示,对于每一个横向的区间,在纵向维护线段树根据纵向......
  • 【luogu CF1109E】Sasha and a Very Easy Test(线段树)
    SashaandaVeryEasyTest题目链接:luoguCF1109E题目大意维护一个长度为n的序列,有区间乘,单点除(保证能整除),区间求和答案对p取模。p不一定是质数。思路麻了考场......
  • 两个和最大的区间(线段树+单调栈+dp)
      胜哥投喂的一道面试题  题意:有一个环形数组\(a\),找出两个不重叠的子串,是的这两个区间内所有的数加起来的和最大。  数据范围:\(1\leqn\leq10^5,\left|......
  • 线段树学习笔记(基础&进阶)(一) | P3372 【模板】线段树 1 题解
    什么是线段树线段树是一棵二叉树,每个结点存储需维护的信息,一般用于处理区间最值、区间和等问题。线段树的用处对编号连续的一些点进行修改或者统计操作,修改和统计的复杂......