bzoj 4695 最假女选手 - segment tree beats

题解:
维护区间最大值mx、最大值出现次数mxcnt、严格次大值semx、最小值mn、最小值出现次数mncnt、严格次小值semn、区间和s、区间加法标记pt、区间最大值相对于其余数字的加法标记mxpt、区间最小值相对于其余数字的加法标记mnpt,一些细节形如:进行朴加法的时候mxpt和mnpt不变;先下传区间加法标记;下传两个最值的标记的时候需要传到最值来源的子树;更新区间最值时,如果发现需要递归左右子树并push_up来得到答案,需要先push_down;注意特判区间最大值等于区间最小值(即所有数字都相等,可以认为是做了一次朴素的加法);注意最大值等于严格次小值或者最小值等于严格次大值(其实本质上都是在说区间只有两类数字的情况,此时以取min为例,若只对区间最大值有影响,而区间最大值等于区间严格次小值,那么在原来的基础上直接对严格次小值进行修改即可)。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define gc getchar()
#define N 500010
#define INF (INT_MAX-600000000)
#define inf (INT_MIN+600000000)
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
    int x=0,s=1,ch;while(((ch=gc)<'0'||ch>'9')&&ch!='-');
    if(ch^'-') x=ch^'0';else s=-1;
    while((ch=gc)>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');return s*x;
}
int a[N];
inline int sgn(int x,int y) { return (x<y)?-1:(x>y); }
struct segment{
    int mn,mx,mnpt,mxpt,pt,l,r,semn,semx,mxcnt,mncnt;lint s;segment *ch[2];
}*rt;
inline int push_up(segment* &rt)
{
    segment *&l=rt->ch[0],*&r=rt->ch[1];
    rt->mnpt=rt->mxpt=rt->pt=0;
    rt->s=l->s+r->s;int d;
    d=sgn(l->mn,r->mn);
    if(d<0) rt->mn=l->mn,rt->mncnt=l->mncnt,rt->semn=min(l->semn,r->mn);
    if(d>0) rt->mn=r->mn,rt->mncnt=r->mncnt,rt->semn=min(r->semn,l->mn);
    if(d==0) rt->mn=l->mn,rt->mncnt=l->mncnt+r->mncnt,rt->semn=min(l->semn,r->semn);
    d=sgn(l->mx,r->mx);
    if(d>0) rt->mx=l->mx,rt->mxcnt=l->mxcnt,rt->semx=max(l->semx,r->mx);
    if(d<0) rt->mx=r->mx,rt->mxcnt=r->mxcnt,rt->semx=max(r->semx,l->mx);
    if(d==0) rt->mx=l->mx,rt->mxcnt=l->mxcnt+r->mxcnt,rt->semx=max(l->semx,r->semx);
    return 0;
}
int build(segment* &rt,int l,int r)
{
    rt=new segment,rt->l=l,rt->r=r,rt->pt=rt->mnpt=rt->mxpt=0;int mid=(l+r)>>1;
    if(l==r) return rt->s=rt->mn=rt->mx=a[l],rt->mxcnt=rt->mncnt=1,rt->semn=INF,rt->semx=inf;
    return build(rt->ch[0],l,mid),build(rt->ch[1],mid+1,r),push_up(rt);
}
int update(segment* &rt,int s,int t,int v,int tp);
int push_down(segment* &rt);
inline int update_tags(segment* &rt,int x)
{
    return rt->pt+=x,rt->mn+=x,rt->mx+=x,rt->semn+=x,rt->semx+=x,rt->s+=(rt->r-rt->l+1ll)*x,0;
}
inline int update_min(segment* &rt,int x)
{
    if(x<=rt->mn) return 0;
    if(x>=rt->semn)
        return push_down(rt),update(rt->ch[0],rt->l,rt->r,x,2),update(rt->ch[1],rt->l,rt->r,x,2),push_up(rt);
    int dlt=x-rt->mn;if(rt->mn==rt->mx) return update_tags(rt,dlt);
    if(rt->semx==rt->mn) rt->semx+=dlt;
    return rt->mnpt+=dlt,rt->mn+=dlt,rt->s+=(lint)dlt*rt->mncnt,0;
}
inline int update_max(segment* &rt,int x)
{
    if(x>=rt->mx) return 0;
    if(x<=rt->semx)
        return push_down(rt),update(rt->ch[0],rt->l,rt->r,x,3),update(rt->ch[1],rt->l,rt->r,x,3),push_up(rt);
    int dlt=rt->mx-x;if(rt->mn==rt->mx) return update_tags(rt,-dlt);
    if(rt->semn==rt->mx) rt->semn-=dlt;
    return rt->mxpt-=dlt,rt->mx-=dlt,rt->s-=(lint)dlt*rt->mxcnt,0;
}
inline int push_down(segment* &rt)
{
    if(rt->pt) update_tags(rt->ch[0],rt->pt),update_tags(rt->ch[1],rt->pt),rt->pt=0;
    if(rt->mnpt)
    {
        int d=sgn(rt->ch[0]->mn,rt->ch[1]->mn);
        if(d<=0) update_min(rt->ch[0],rt->ch[0]->mn+rt->mnpt);
        if(d>=0) update_min(rt->ch[1],rt->ch[1]->mn+rt->mnpt);
        rt->mnpt=0;
    }
    if(rt->mxpt)
    {
        int d=sgn(rt->ch[0]->mx,rt->ch[1]->mx);
        if(d>=0) update_max(rt->ch[0],rt->ch[0]->mx+rt->mxpt);
        if(d<=0) update_max(rt->ch[1],rt->ch[1]->mx+rt->mxpt);
        rt->mxpt=0;
    }
    return 0;
}
int update(segment* &rt,int s,int t,int x,int tp)//tp=1,plus; tp=2,x=max(x,v); tp=3,x=min(x,v)
{
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(tp==2&&rt->mn>=x) return 0;
    if(tp==3&&rt->mx<=x) return 0;
    if(s<=l&&r<=t)
    {
        if(tp==1) update_tags(rt,x);
        if(tp==2) update_min(rt,x);
        if(tp==3) update_max(rt,x);
        return 0;
    }
    push_down(rt);
    if(s<=mid) update(rt->ch[0],s,t,x,tp);
    if(mid<t) update(rt->ch[1],s,t,x,tp);
    return push_up(rt);
}
lint query(segment* &rt,int s,int t,int tp)//tp=1,sum; tp=2,max; tp=3,min;
{
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(s<=l&&r<=t)
    {
        if(tp==1) return rt->s;
        if(tp==2) return rt->mx;
        if(tp==3) return rt->mn;
    }
    push_down(rt);lint ans=0;
    if(tp==2) ans=inf;
    if(tp==3) ans=INF;
    if(s<=mid)
    {
        lint v=query(rt->ch[0],s,t,tp);
        if(tp==1) ans+=v;
        if(tp==2) ans=max(ans,v);
        if(tp==3) ans=min(ans,v);
    }
    if(mid<t)
    {
        lint v=query(rt->ch[1],s,t,tp);
        if(tp==1) ans+=v;
        if(tp==2) ans=max(ans,v);
        if(tp==3) ans=min(ans,v);
    }
    return ans;
}
int main()
{
    int n=inn();
    for(int i=1;i<=n;i++) a[i]=inn();
    build(rt,1,n);
    for(int q=inn();q;q--)
    {
        int tp=inn(),l=inn(),r=inn();
        if(tp<=3) update(rt,l,r,inn(),tp);
        else printf("%lld\n",query(rt,l,r,tp-3));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值