【模板】吉司机线段树 HDU 5306 Gorgeous Sequence

也叫小清新线段树,用于解决区间最值修改问题

具体可以参考jiry_2神犇的集训队论文和WC2016上的PPT

此题就作为模板好了,模板的话写法是比较精妙的

#include<bits/stdc++.h>
using namespace std;

#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
#define mem(a,b) memset(a,b,sizeof(a))
#define int long long

const int N=1000000+10;

int n,m,a[N];
struct tree{//其实更新标记和最大值可以二合一
//若更新成功则最大值就是标记,若没有更新下传最大值也不会更新子区间
    int l,r,mx,se,c,sum;
    #define l(i) t[i].l
    #define r(i) t[i].r
    #define c(i) t[i].c
    #define mx(i) t[i].mx
    #define se(i) t[i].se
    #define sum(i) t[i].sum
    #define ls rt<<1
    #define rs rt<<1|1
}t[N<<2];

template<typename T>void read(T &x){
    x=0;char c=getchar(),f=1;
    while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); }
    while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); }
    x*=f;
}

void push_up(int rt){
    sum(rt)=sum(ls)+sum(rs);
    mx(rt)=max(mx(ls),mx(rs));
    se(rt)=max(se(ls),se(rs));
    if(mx(ls)!=mx(rs)) se(rt)=max(se(rt),min(mx(ls),mx(rs)));
    if(mx(rt)==mx(ls)) c(rt)+=c(ls);
    if(mx(rt)==mx(rs)) c(rt)+=c(rs);
    //注意啦,此处写法十分精妙。
    //若mx(ls)==mx(rs) c(rt)=c(ls)+c(rs)
}

void update(int rt,int z){//在下传标记时z不可能<=se(rt),因为Z>se(rt的祖先节点)>=se(rt)
    if(z>=mx(rt)) return;
    sum(rt)-=(mx(rt)-z)*c(rt);
    mx(rt)=z;
}

void build(int rt,int l,int r){
    l(rt)=l,r(rt)=r;
    if(l==r){
        mx(rt)=sum(rt)=a[l],se(rt)=-1;
        c(rt)=1;
        return;
    }
    int mid=l+r>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    push_up(rt);
}

void push_down(int rt){
    update(ls,mx(rt)),update(rs,mx(rt));
}

void change(int rt,int x,int y,int z){
    if(z>=mx(rt)) return;//剪枝,如果比大区间的最大值还大,自然不可能修改小区间
    if(l(rt)>=x&&r(rt)<=y&&z>se(rt)){
        update(rt,z);
        return;
    }
    int mid=l(rt)+r(rt)>>1;
    push_down(rt);
    if(x<=mid) change(ls,x,y,z);
    if(y>mid) change(rs,x,y,z);
    push_up(rt);
}

int getmax(int rt,int x,int y){
    if(l(rt)>=x&&r(rt)<=y) return mx(rt);
    push_down(rt);
    int ans=0;
    int mid=l(rt)+r(rt)>>1;
    if(x<=mid) ans=max(ans,getmax(ls,x,y));
    if(y>mid) ans=max(ans,getmax(rs,x,y));
    return ans;
}

int getsum(int rt,int x,int y){
    if(l(rt)>=x&&r(rt)<=y) return sum(rt);
    push_down(rt);
    int ans=0,mid=l(rt)+r(rt)>>1;
    if(x<=mid) ans+=getsum(ls,x,y);
    if(y>mid) ans+=getsum(rs,x,y);
    return ans;
}

void work(){
    read(n),read(m);
    go(i,1,n) read(a[i]);
    build(1,1,n);
    int op,x,y,z;
    while(m--){
        read(op),read(x),read(y);
        if(!op) read(z),change(1,x,y,z);
        else if(op==1) printf("%lld\n",getmax(1,x,y));
        else if(op==2) printf("%lld\n",getsum(1,x,y));
    }
}

signed main(){
    //freopen("data.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int T;read(T);
    while(T--) work();
    return 0;
}

原文地址:https://www.cnblogs.com/White-star/p/11508170.html

时间: 2024-11-11 00:33:43

【模板】吉司机线段树 HDU 5306 Gorgeous Sequence的相关文章

hdu 5306 Gorgeous Sequence(线段树)

题目链接:hdu 5306 Gorgeous Sequence 和普通的线段树一样一个标记值T,然后另外加一个C值记录以u为根节点的子树下有多少个叶子节点被T值控制.每次修改时,dfs到叶子节点之后在修改该节点.维护sum值时只要额外加上T值控制下的节点.加了个输入外挂,时间少了将近1000ms. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const in

bzoj4355 Play with sequence(吉司机线段树)题解

题意: 已知\(n\)个数字,进行以下操作: \(1.\)区间\([L,R]\) 赋值为\(x\) \(2.\)区间\([L,R]\) 赋值为\(max(a[i] + x, 0)\) \(3.\)区间\([L,R]\) 询问\(0\)个数 已知初始值\(\geq 0\),\(x\geq0\). 思路: 吉司机线段树. 操作\(1\)可以直接打覆盖标记. 操作\(2\)可以分为两步:区间加\(x\),然后取区间\(max(a[i],0)\). 操作\(3\)只要维护最小值的个数,因为不管怎么操作最

hdu 5306 Gorgeous Sequence(区间最值更新+求和)

题目链接:hdu 5306 Gorgeous Sequence 题意: 给你一个序列,有三种操作. 0 x y t:将[x,y]的数取min(a[i],t) 1 x y:求[x,y]的最大值 2 x y:求[x,y]的区间和 题解: 吉老师的课件题:传送门 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;i++) 3 #define ls l,m,rt<<1 4 #define rs m+1,r,rt

hdu 5306 Gorgeous Sequence(暴力线段树)(好题)

题意:区间最大长度为1000000, 三种操作: 区间求和: 区间求最大值: 将节点值更新为当前值与给定值中的最小值(有趣的更新): 思路: 暴力线段树.关键在于处理标记,需要维护最大标记,标记覆盖范围,所在区间: 覆盖区域标记起到很关键的作用: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct node{ long long sum; int tag,m

HDU 5306 Gorgeous Sequence[线段树区间最值操作]

Gorgeous Sequence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 2150    Accepted Submission(s): 594 Problem Description There is a sequence a of length n. We use ai to denote the i-th elemen

HDU 5306 Gorgeous Sequence

参考 关键是加一个标记cv:这个区间有多少个结点,已被 tag 影响. Gorgeous Sequence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 396    Accepted Submission(s): 78 Problem Description There is a sequence a of length n.

HDU 5306 吉司机线段树

思路: 后面nlogn的部分是伪证... 大家可以构造数据证明是这是nlog^2n的啊~ 吉老司机翻车了 //By SiriusRen #include <cstdio> #include <algorithm> using namespace std; const int N=1000050; int cases,n,m,op,xx,yy,zz; typedef long long ll; struct SegTree{int max1,max2,maxnum,lazy;ll s

hdu6521 吉司机线段树

http://acm.hdu.edu.cn/showproblem.php?pid=6521 待填 代码 #include<bits/stdc++.h> #define ls o<<1 #define rs o<<1|1 #define ll long long using namespace std; const int MAXN = 5e5+5; int Mx[MAXN<<2],Mx2[MAXN<<2],Mn[MAXN<<2],l

吉司机线段树思路整理[bzoj4355 Play with sequence]

原题目链接 原代码来自attack #include<cstdio> #include<algorithm> #define LL long long //#define int long long using namespace std; const int MAXN = 3 * 1e5 + 10; const LL INF = 1e10 +10; inline int read() { char c = getchar(); int x = 0, f = 1; while(c