2018.07.25 bzoj3878: [Ahoi2014&Jsoi2014]奇怪的计算器(线段树)

传送门

线段树综合。

让我想起一道叫做siano" role="presentation" style="position: relative;">sianosiano的题,这题就是那题的强化版本。

说说做法吧:

跟siano" role="presentation" style="position: relative;">sianosiano一样,当我们把a[i]" role="presentation" style="position: relative;">a[i]a[i]排成有序的之后,就会保证在若干次操作后整个数列仍然是单调的。

首先加减可以看成一个操作,L,R" role="presentation" style="position: relative;">L,RL,R的限制也只相当于一个操作,因此这道题要我们维护这几个操作:

1.区间加

2.区间乘

3.区间加变形(加上原数a[i]" role="presentation" style="position: relative;">a[i]a[i]的k" role="presentation" style="position: relative;">kk倍)

4.区间覆盖

维护:区间最大最小,单点值。

因此我们可以设计一个神奇的更新函数f(p,k1,k2,k3)" role="presentation" style="position: relative;">f(p,k1,k2,k3)f(p,k1,k2,k3),表示整个区间的值的变化方式:

c[i]=c[i]∗k1+k2∗a[i]+k3" role="presentation" style="position: relative;">c[i]=c[i]∗k1+k2∗a[i]+k3c[i]=c[i]∗k1+k2∗a[i]+k3,然后我们又可以惊奇的发现这个函数可以用于所有的区间修改函数。

1.区间加:f(p,1,0,add)" role="presentation" style="position: relative;">f(p,1,0,add)f(p,1,0,add)。

2.区间乘:f(p,mul,0,0)" role="presentation" style="position: relative;">f(p,mul,0,0)f(p,mul,0,0)。

3.区间加变形:f(p,1,addx,0)" role="presentation" style="position: relative;">f(p,1,addx,0)f(p,1,addx,0)。

4.区间覆盖:f(p,0,0,set)" role="presentation" style="position: relative;">f(p,0,0,set)f(p,0,0,set)。

这样的话,我们连前三个操作的单独的修改函数都不用写,简洁自然。

代码如下:

#include<bits/stdc++.h>
#define ll long long
#define N 100005
#define lc (p<<1)
#define rc (p<<1|1)
#define p1 T[p].lz1
#define p2 T[p].lz2
#define p3 T[p].lz3
#define mid (T[p].l+T[p].r>>1)
using namespace std;
struct Node{int l,r;ll mn,mx,lz1,lz2,lz3;}T[N<<2];
struct Query{int op;ll v;}q[N];
struct Ans{int id;ll v;}a[N];
int n,m;
ll ans[N],L,R;
inline ll max(ll a,ll b){return a>b?a:b;}
inline ll min(ll a,ll b){return a<b?a:b;}
inline void pushup(int p){T[p].mx=T[rc].mx,T[p].mn=T[lc].mn;}
inline void pushnow(int p,ll k1,ll k2,ll k3){
    p1*=k1,p2=p2*k1+k2,p3=p3*k1+k3;
    T[p].mx=T[p].mx*k1+k2*a[T[p].r].v+k3;
    T[p].mn=T[p].mn*k1+k2*a[T[p].l].v+k3;
}
inline void pushdown(int p){pushnow(lc,p1,p2,p3),pushnow(rc,p1,p2,p3),p1=1,p2=0,p3=0;}
inline void build(int p,int l,int r){
    T[p].l=l,T[p].r=r,p1=1,p2=p3=0,T[p].mx=a[r].v,T[p].mn=a[l].v;
    if(l==r)return;
    build(lc,l,mid),build(rc,mid+1,r);
}
inline void modify1(int p){
    if(T[p].l==T[p].r)return pushnow(p,0,0,L);
    pushdown(p);
    if(T[rc].mn<L)pushnow(lc,0,0,L),modify1(rc);
    else modify1(lc);
    pushup(p);
}
inline void modify2(int p){
    if(T[p].l==T[p].r)return pushnow(p,0,0,R);
    pushdown(p);
    if(T[lc].mx>R)pushnow(rc,0,0,R),modify2(lc);
    else modify2(rc);
    pushup(p);
}
inline bool cmp(Ans a,Ans b){return a.v<b.v;}
inline void query(int p){
    if(T[p].l==T[p].r){ans[a[T[p].l].id]=T[p].mn;return;}
    pushdown(p),query(lc),query(rc);
}
int main(){
    scanf("%d%lld%lld",&m,&L,&R);
    for(int i=1;i<=m;++i){
        char s[2];
        scanf("%s%lld",s,&q[i].v);
        switch(s[0]){
            case '+':{q[i].op=1;break;}
            case '-':{q[i].op=2;break;}
            case '*':{q[i].op=3;break;}
            default:{q[i].op=4;break;}
        }
    }
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%lld",&a[i].v),a[i].id=i;
    sort(a+1,a+n+1,cmp),build(1,1,n);
    for(int i=1;i<=m;++i){
        switch(q[i].op){
            case 1:{pushnow(1,1,0,q[i].v);break;}
            case 2:{pushnow(1,1,0,-q[i].v);break;}
            case 3:{pushnow(1,q[i].v,0,0);break;}
            default:{pushnow(1,1,q[i].v,0);break;}
        }
        if(T[1].mn<L)modify1(1);
        if(T[1].mx>R)modify2(1);
    }
    query(1);
    for(int i=1;i<=n;++i)cout<<ans[i]<<'\n';
    return 0;
}
上一篇:【loj6029】「雅礼集训 2017 Day1」市场 线段树+均摊分析


下一篇:Oracle 迁移一个带lob的表到另一个表空间