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

吉司机线段树

时间:2023-09-05 16:48:20浏览次数:41  
标签:tag1 线段 mid 司机 push tag 区间 最大值

一、区间历史最值

以区间历史最大值为例。首先,相应地,设 \(maxb\) 表示一个节点的区间历史最大值。为了更新一个区间的子区间,再设一个 \(tag2\) ,表示 \(tag1\) 从上次 \(push\_down\) 以后到现在达到过的最大值。

\(code:\)

void push_up(int u){
    p[u].w=p[u<<1].w+p[u<<1|1].w;
    p[u].maxa=max(p[u<<1].maxa,p[u<<1|1].maxa);
    p[u].maxb=max(p[u<<1].maxb,p[u<<1|1].maxb);
}
void push_down(int u){//push_down时,注意先下传maxb,tag2,后下传maxa,tag1
    p[u<<1].w+=(p[u<<1].r-p[u<<1].l+1)*p[u].tag1;
    p[u<<1|1].w+=(p[u<<1|1].r-p[u<<1|1].l+1)*p[u].tag1;
    p[u<<1].maxb=max(p[u<<1].maxb,p[u<<1].maxa+p[u].tag2);
    p[u<<1|1].maxb=max(p[u<<1|1].maxb,p[u<<1|1].maxa+p[u].tag2);
    p[u<<1].tag2=max(p[u<<1].tag2,p[u<<1].tag1+p[u].tag2);
    p[u<<1|1].tag2=max(p[u<<1|1].tag2,p[u<<1|1].tag1+p[u].tag2);
    p[u<<1].maxa+=p[u].tag1;
    p[u<<1|1].maxa+=p[u].tag1;
    p[u<<1].tag1+=p[u].tag1;
    p[u<<1|1].tag1+=p[u].tag1;
    p[u].tag1=p[u].tag2=0;
}
void build(int u,int l,int r){
    p[u].l=l;p[u].r=r;
    if(l==r){
        p[u].w=p[u].maxa=p[u].maxb=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(u<<1,l,mid);build(u<<1|1,mid+1,r);
    push_up(u);
}
void add(int u,int l,int r,int w){
    if(p[u].l>=l&&p[u].r<=r){
        p[u].w+=(p[u].r-p[u].l+1)*w;
        p[u].tag1+=w;
        p[u].maxa+=w;
        p[u].tag2=max(p[u].tag2,p[u].tag1);
        p[u].maxb=max(p[u].maxb,p[u].maxa);
        return ;
    }
    push_down(u);
    int mid=(p[u].l+p[u].r)>>1;
    if(mid>=l)
        add(u<<1,l,r,w);
    if(mid<r)
        add(u<<1|1,l,r,w);
    push_up(u);
}
int ask(int u,int l,int r,int opt){
    if(p[u].l>=l&&p[u].r<=r){
        if(opt==1) return p[u].w;
        else if(opt==2) return p[u].maxa;
        else return p[u].maxb;
    }
    push_down(u);
    int re=0,maxx=-1e18,mid=(p[u].l+p[u].r)>>1;
    if(mid>=l){
        if(opt==1) re+=ask(u<<1,l,r,opt);
        else maxx=max(maxx,ask(u<<1,l,r,opt));
    }
    if(mid<r){
        if(opt==1) re+=ask(u<<1|1,l,r,opt);
        else maxx=max(maxx,ask(u<<1|1,l,r,opt));
    }
    if(opt==1) return re;
    else return maxx;
}

区间最值操作

以区间最小值操作,即把区间的所有数变成 \(min(w,a[i])\) 为例。设 \(max2\) 表示一个区间的严格次大值(不等于最大值), \(cnt\) 表示一个区间的最大值的个数。在进行区间最值操作时时,设给定的数为 \(w\) ,那么只有当 \(max2<w<maxa\) 时,才对当前区间更新。

此外,可以发现,该操作会导致区间最大值的 \(tag\) 和其他值的 \(tag\) 不一样。此时需要设四个 \(tag\) 。

设 \(tag1\) 表示区间最大值的 \(tag\) , \(tag2\) 表示区间次大值的 \(tag\) ; \(tag3\) 表示 \(tag1\) 从上次 \(push\_down\) 以后达到过的最大值; \(tag4\) 表示 \(tag2\) 从上次 \(push\_down\) 以后达到过的最大值。

\(code:\)

void push_up(int u){
    p[u].w=p[u<<1].w+p[u<<1|1].w;
    p[u].maxa=max(p[u<<1].maxa,p[u<<1|1].maxa);
    p[u].maxb=max(p[u<<1].maxb,p[u<<1|1].maxb);
    if(p[u<<1].maxa==p[u<<1|1].maxa){
        p[u].max2=max(p[u<<1].max2,p[u<<1|1].max2);
        p[u].cnt=p[u<<1].cnt+p[u<<1|1].cnt;
    }
    else if(p[u<<1].maxa>p[u<<1|1].maxa){
        p[u].max2=max(p[u<<1].max2,p[u<<1|1].maxa);
        p[u].cnt=p[u<<1].cnt;
    }
    else{
        p[u].max2=max(p[u<<1|1].max2,p[u<<1].maxa);
        p[u].cnt=p[u<<1|1].cnt;
    }
}
void change(int k1,int k2,int k3,int k4,int u){
    p[u].w+=k1*p[u].cnt+k2*(p[u].r-p[u].l+1-p[u].cnt);
    p[u].maxb=max(p[u].maxb,p[u].maxa+k3);
    p[u].maxa+=k1;
    if(p[u].max2!=-1e18)
        p[u].max2+=k2;
    p[u].tag3=max(p[u].tag3,p[u].tag1+k3);
    p[u].tag4=max(p[u].tag4,p[u].tag2+k4);
    p[u].tag1+=k1;p[u].tag2+=k2;
}
void push_down(int u){//push_down时,注意先下传maxb,tag2,后下传maxa,tag1
    int maxx=max(p[u<<1].maxa,p[u<<1|1].maxa);
    if(p[u<<1].maxa==maxx)
        change(p[u].tag1,p[u].tag2,p[u].tag3,p[u].tag4,u<<1);
    else
        change(p[u].tag2,p[u].tag2,p[u].tag4,p[u].tag4,u<<1);
    if(p[u<<1|1].maxa==maxx)
        change(p[u].tag1,p[u].tag2,p[u].tag3,p[u].tag4,u<<1|1);
    else
        change(p[u].tag2,p[u].tag2,p[u].tag4,p[u].tag4,u<<1|1);
    p[u].tag1=p[u].tag2=p[u].tag3=p[u].tag4=0;
}
void build(int u,int l,int r){
    p[u].l=l;p[u].r=r;
    if(l==r){
        p[u].w=p[u].maxa=p[u].maxb=a[l];
        p[u].cnt=1;p[u].max2=-1e18;
        return ;
    }
    int mid=(l+r)>>1;
    build(u<<1,l,mid);build(u<<1|1,mid+1,r);
    push_up(u);
}
void add(int u,int l,int r,int w){
    if(p[u].l>=l&&p[u].r<=r){
        p[u].w+=(p[u].r-p[u].l+1)*w;
        p[u].maxa+=w;
        p[u].maxb=max(p[u].maxb,p[u].maxa);
        if(p[u].max2!=-1e18)
            p[u].max2+=w;
        p[u].tag1+=w;p[u].tag2+=w;
        p[u].tag3=max(p[u].tag3,p[u].tag1);
        p[u].tag4=max(p[u].tag4,p[u].tag2);
        return ;
    }
    push_down(u);
    int mid=(p[u].l+p[u].r)>>1;
    if(mid>=l)
        add(u<<1,l,r,w);
    if(mid<r)
        add(u<<1|1,l,r,w);
    push_up(u);
}
void minn(int u,int l,int r,int w){
    if(p[u].maxa<=w)
        return ;
    if(p[u].l>=l&&p[u].r<=r&&p[u].max2<w){
        int k=p[u].maxa-w;
        p[u].w-=p[u].cnt*k;
        p[u].maxa=w;p[u].tag1-=k;
        return ;
    }
    push_down(u);
    int mid=(p[u].l+p[u].r)>>1;
    if(mid>=l)
        minn(u<<1,l,r,w);
    if(mid<r)
        minn(u<<1|1,l,r,w);
    push_up(u);
}
int ask(int u,int l,int r,int opt){
    if(p[u].l>=l&&p[u].r<=r){
        if(opt==1) return p[u].w;
        else if(opt==2) return p[u].maxa;
        else return p[u].maxb;
    }
    push_down(u);
    int re=0,maxx=-1e18,mid=(p[u].l+p[u].r)>>1;
    if(mid>=l){
        if(opt==1) re+=ask(u<<1,l,r,opt);
        else maxx=max(maxx,ask(u<<1,l,r,opt));
    }
    if(mid<r){
        if(opt==1) re+=ask(u<<1|1,l,r,opt);
        else maxx=max(maxx,ask(u<<1|1,l,r,opt));
    }
    if(opt==1) return re;
    else return maxx;
}

P4314 CPU 监控

题目要求维护一个支持区间推平,区间加,区间历史最值的线段树。

设 \(tag1\) 表示区间加的 \(tag\) , \(tag2\) 表示 \(tag1\) 的最大值, \(tag3\) 表示区间推平的 \(tag\) , \(tag4\) 表示 \(tag3\) 的最大值,然后就可以维护了。情况比较复杂,不要漏掉或写错每一个情况。另外,别忘了把 \(tag4\) 的初始值设为 \(-INF\) 。

\(code:\)

void push_down(int u){
    if(p[u].ok){
        p[u<<1].maxb=max(p[u<<1].maxb,max(p[u<<1].maxa+p[u].tag2,p[u].tag4));
        p[u<<1|1].maxb=max(p[u<<1|1].maxb,max(p[u<<1|1].maxa+p[u].tag2,p[u].tag4));
        p[u<<1].maxa=p[u<<1|1].maxa=p[u].tag3;
        p[u<<1].tag4=max(p[u<<1].tag4,p[u].tag4);
        p[u<<1|1].tag4=max(p[u<<1|1].tag4,p[u].tag4);
        if(!p[u<<1].ok)
            p[u<<1].tag2=max(p[u<<1].tag2,p[u<<1].tag1+p[u].tag2);
        else
            p[u<<1].tag4=max(p[u<<1].tag4,p[u<<1].tag3+p[u].tag2);
        if(!p[u<<1|1].ok)
            p[u<<1|1].tag2=max(p[u<<1|1].tag2,p[u<<1|1].tag1+p[u].tag2);
        else
            p[u<<1|1].tag4=max(p[u<<1|1].tag4,p[u<<1|1].tag3+p[u].tag2);
        p[u<<1].tag3=p[u<<1|1].tag3=p[u].tag3;
        p[u<<1].ok=p[u<<1|1].ok=1;
    }
    else{
        p[u<<1].maxb=max(p[u<<1].maxb,p[u<<1].maxa+p[u].tag2);
        p[u<<1|1].maxb=max(p[u<<1|1].maxb,p[u<<1|1].maxa+p[u].tag2);
        p[u<<1].maxa+=p[u].tag1;
        p[u<<1|1].maxa+=p[u].tag1;
        if(p[u<<1].ok){
            p[u<<1].tag4=max(p[u<<1].tag4,p[u<<1].tag3+p[u].tag2);
            p[u<<1].tag3+=p[u].tag1;
        }
        else{
            p[u<<1].tag2=max(p[u<<1].tag2,p[u<<1].tag1+p[u].tag2);
            p[u<<1].tag1+=p[u].tag1;
        }
        if(p[u<<1|1].ok){
            p[u<<1|1].tag4=max(p[u<<1|1].tag4,p[u<<1|1].tag3+p[u].tag2);
            p[u<<1|1].tag3+=p[u].tag1;
        }
        else{
            p[u<<1|1].tag2=max(p[u<<1|1].tag2,p[u<<1|1].tag1+p[u].tag2);
            p[u<<1|1].tag1+=p[u].tag1;
        }
    }
    p[u].ok=0;p[u].tag1=p[u].tag3=p[u].tag2=0;p[u].tag4=-1e18;
}

标签:tag1,线段,mid,司机,push,tag,区间,最大值
From: https://www.cnblogs.com/andyl/p/17680062.html

相关文章

  • 李超线段树学习笔记
    李超线段树学习笔记P4097【模板】李超线段树/[HEOI2013]Segment题意要求在平面直角坐标系下维护两个操作:在平面上加入一条线段。记第\(i\)条被插入的线段的标号为\(i\)。给定一个数\(k\),询问与直线\(x=k\)相交的线段中,交点纵坐标最大的线段的编号。做法首先,......
  • 线段树
    建树:inta[100005],d[100005];voidbuild(ints,inte,intp){//建树 //对区间[s,t]建立线段树,当前根编号为p if(s==e){ d[p]=a[s]; return; } intm=s+((e-s)>>1); build(s,m,p*2); build(m+1,e,p*2+1);//分割出两个子区间 d[p]=d[p*2]+d[(p*2)+1];}//d[i]为......
  • P3373 【模板】线段树 2
    【模板】线段树2如题,已知一个数列,你需要进行下面三种操作:将某区间每一个数乘上\(x\);将某区间每一个数加上\(x\);求出某区间每一个数的和。输入格式第一行包含三个整数\(n,q,m\),分别表示该数列数字的个数、操作的总个数和模数。第二行包含\(n\)个用空格分隔的整数,其......
  • Daimayuan Online Judge 线段树打标记2
    给\(n\)个数\(a_1,a_2,\cdots,a_n\)。支持\(q\)个操作:1lrd,令所有的\(a_i(l\leqi\leqr)\)加上\(d\)。2lrd,令所有的\(a_i(l\leqi\leqr)\)乘上\(d\)。3lrd,令所有的\(a_i(l\leqi\leqr)\)等于\(d\)。4lr,查询\((\sum_{i=l......
  • Daimayuan Online Judge 线段树打标记1
    给\(n\)个数\(a_1,a_2,\cdots,a_n\)。支持\(q\)个操作:1lrd,令所有的\(a_i(l\leqi\leqr)\)加上\(d\)。2lr,查询\(max_{i=l}^{r}a_i\)。区间修改的线段树要比基础线段树多考虑一个元素:\(lazy\tag\)。复杂的信息可以用多个标记表示。\(lazy\ta......
  • Daimayuan Online Judge 线段树2
    给\(n\)个数\(a_1,a_2,\cdots,a_n\)。支持\(q\)个操作:1xd,修改\(a_x=d\)。2lr,查询\([l,r]\)中的最大子段和。一:确定需要维护的信息。根据分治中线讨论,哪些信息可以合并出所需信息。递归讨论新信息如何合并。直至完全拆解。不越过分治中线:\([l,r]\)......
  • Daimayuan Online Judge 线段树1
    给\(n\)个数\(a_1,a_2,\cdots,a_n\)。支持\(q\)个操作:1.1xd,修改\(a_x=d\)。2.2lr,查询\(min_{i=l}^{r}a_i\),并输出\(\sum_{i=l}^{r}[a_i=min_{i=l}^{r}a_i]\)。一:确定出需要维护的信息\(Info\)。建立线段树节点structInfo{ intminv......
  • 线段树
    下饭写错合集:if(l>R||r<L)return0;写成if(l>=R||r<=L)return0;t1[p]没有初始化为\(1\)。忘记建树QAQ。线段树是解决RMQ问题中的一种常用的数据结构,树状数组能实现的功能是线段树能实现功能的子集。线段树可以在\(O(\logn)\)内实现。单点/区间修改(加,......
  • 树上可持久化线段树
    例题传送门:Countonatree简要题意:有棵\(n\)个节点的树,每次点有个权值\(a_i\),每次询问给出\(u,v,k\),求\(u,v\)两个节点的简单路径上(包括\(u,v\))上第\(k\)小的点,保证数据有解,强制在线\(1\len,m\le10^5,a_i\in[1,2^{31}-1]\)首先,第\(k\)小就可以想到要可持久化线段树,动态开......
  • P2824 排序(二分答案加线段树)
    传送门很巧妙的一个题直接排序肯定会\(T\)飞我们发现问题只有一个:第\(q\)个位置上的数字不难想到从这里入手,二分答案,考虑第\(q\)个位置上的数字是什么,不妨设他为\(x\)然后把大于等于\(x\)的数变成\(1\),小于\(x\)的数变为\(0\),把他转换为一个\(01\)排序问题:对于区间\([l,r]\)......