序列操作 - 线段树

题目大意:你要维护一个长度为n的序列,进行操作。
对于这个序列,233之类的数不能出现,也就是说233,2333,23333,233333……这一系列的数不能在序列中出现。
1 i 输出第i个元素。
2 a b x 将[a,b]区间的序列赋值为x。
3 a b x 将[a,b]区间的序列加上x。
4 a b 将区间[a,b]赋值为[a,b]中的最大值。
5 a b 将区间[a,b]赋值为[a,b]中的最小值。
6 a b 将区间[a,b]赋值为[a,b]中的平均值,平均值向下取整。
如果在操作后区间中出现了233之类的数,则需要将你刚才操作的那个区间的数全部加一,如果再出现,就再加一,直到不出现为止。
初始不会出现可怕的数字。数据范围1e5,数值1e9+7。

题解:
分级,相同的区间视为缩成一个点,这样复杂度不会超过两个log。(其实平衡树可以严格一个log但是常数大反而跑得不是很快。)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const lint INF=LLONG_MAX/10-10;const int N=100010;
lint kp[20];int kpc,a[N];
struct segment{
    int l,r,pos;lint pt,vt;
    lint mn,mx,mndis,s;
    segment *ch[2];
}*rt;
inline lint getnxt(lint x)
{
    rep(i,0,kpc) if(kp[i]>=x) return kp[i];
    return INF;
}
inline int push_up(segment* &rt)
{
    rt->s=rt->ch[0]->s+rt->ch[1]->s,
    rt->mn=min(rt->ch[0]->mn,rt->ch[1]->mn),
    rt->mx=max(rt->ch[0]->mx,rt->ch[1]->mx),
    rt->mndis=min(rt->ch[0]->mndis,rt->ch[1]->mndis);
    if(rt->ch[0]->mndis<rt->ch[1]->mndis)
        rt->pos=rt->ch[0]->pos;
    else rt->pos=rt->ch[1]->pos;return 0;
}
int build(segment* &rt,int l,int r)
{
    rt=new segment,rt->l=l,rt->r=r,rt->pt=0,rt->vt=-1;int mid=(l+r)>>1;
    if(l==r) return rt->mndis=getnxt(rt->mn=rt->mx=rt->s=a[rt->pos=l])-a[l],0;
    return build(rt->ch[0],l,mid),build(rt->ch[1],mid+1,r),push_up(rt);
}
inline int update_tags(segment* &rt,lint v,int tp)
{
    int len=rt->r-rt->l+1;
    if(tp==1)
    {
        rt->mn=rt->mx=v,rt->s=v*len,rt->vt=v,
        rt->pt=0,rt->mndis=getnxt(v)-v,rt->pos=rt->l;
    }
    else{
        rt->mn+=v,rt->mx+=v,rt->s+=v*len,rt->mndis-=v;
        if(rt->vt>=0) rt->vt+=v;else rt->pt+=v;
    }
    return 0;
}
inline int push_down(segment* &rt)
{
    if(rt->vt>=0)
    {
        update_tags(rt->ch[0],rt->vt,1),
        update_tags(rt->ch[1],rt->vt,1),
        rt->vt=-1;
    }
    else if(rt->pt)
    {
        update_tags(rt->ch[0],rt->pt,2),
        update_tags(rt->ch[1],rt->pt,2),
        rt->pt=0;
    }
    return 0;
}
int update(segment* &rt,int s,int t,lint v,int tp)
{
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(s<=l&&r<=t) return update_tags(rt,v,tp);
    if(rt->pt||rt->vt>=0) push_down(rt);
    if(s<=mid) update(rt->ch[0],s,t,v,tp);
    if(mid<t) update(rt->ch[1],s,t,v,tp);
    return push_up(rt);
}
lint query(segment* &rt,int s,int t,int tp)
{
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(s<=l&&r<=t) return tp==1?rt->mx:(tp==2?rt->mn:rt->s);
    if(rt->pt||rt->vt>=0) push_down(rt);lint ans;
    ans=(tp==1?LLONG_MIN:(tp==2?LLONG_MAX:0));
    if(s<=mid)
    {
        lint v=query(rt->ch[0],s,t,tp);
        ans=(tp==1?max(ans,v):(tp==2?min(ans,v):ans+v));
    }
    if(mid<t)
    {
        lint v=query(rt->ch[1],s,t,tp);
        ans=(tp==1?max(ans,v):(tp==2?min(ans,v):ans+v));
    }
    return ans;
}
int update_dis(segment* &rt,int p)
{
    if(rt->mn==rt->mx) return rt->mndis=getnxt(rt->mx)-rt->mx,0;
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(rt->pt||rt->vt>=0) push_down(rt);
    return update_dis(rt->ch[p>mid],p),push_up(rt);
}
inline int adj(int l,int r)
{
    while(rt->mndis<=0)
    {
        while(rt->mndis<0) update_dis(rt,rt->pos);
        while(rt->mndis==0) update(rt,l,r,1,2);
    }
    return 0;
}
int main()
{
    kp[0]=233,kpc=16;rep(i,1,kpc) kp[i]=kp[i-1]*10+3;
    int n=inn(),q=inn();
    rep(i,1,n) a[i]=inn();
    build(rt,1,n);
    while(q--)
    {
        int tp=inn();lint x;
        if(tp==1) { int p=inn();printf("%lld\n",query(rt,p,p,3));continue; }
        int l=inn(),r=inn();
        if(tp<=3) x=inn();
        else x=query(rt,l,r,tp-3);
        if(tp==6) x/=(r-l+1);
        if(tp==3) update(rt,l,r,x,2),adj(l,r);
        else update(rt,l,r,getnxt(x)==x?x+1:x,1);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值