#164. 【清华集训2015】V

Description

Picks博士观察完金星凌日后,设计了一个复杂的电阻器。为了简化题目,题目中的常数与现实世界有所不同。

这个电阻器内有编号为 1∼n1∼n 的 nn 个独立水箱,水箱呈圆柱形,底面积为 1 m21 m2,每个水箱在顶部和底部各有一个阀门,可以让水以 1 m3/s1 m3/s 的流量通过,每个水箱的上阀门接水龙头,可以无限供应水,下阀门不接东西,可以让水流出。水箱顶部和底部都有一个接口,水的电阻率为 1 Ω⋅m1 Ω⋅m。

水箱的高度足够高,有一个导电浮标浮在水面上,通过导线与水箱顶的接口相连。一开始时第 ii 个水箱中有 ai m3ai m3 的水。

Picks博士接下来就需要对这个复杂的电阻器进行调试。他会进行以下五种操作。

1、打开编号在 [l,r][l,r] 中的所有水箱的上方阀门 xx 秒,然后关上它们的上方阀门。

2、打开编号在 [l,r][l,r] 中的所有水箱的下方阀门 xx 秒,然后关上它们的下方阀门。

3、将编号在 [l,r][l,r] 中的所有水箱的下方阀门与大海通过连通器以一定方式相连,使得这些水箱中都恰拥有 x m3x m3 的水,然后关上它们的下方阀门,撤去连通器。

4、在第 yy 个水箱的上下方接口处接上一个电动势为 1 V1 V 的电源,电源没有内阻,Picks博士会测量出通过电源的电流大小,之后撤去该电源。

5、由于水浸泡过的地方会留下明显的水渍而没有被水浸泡过的地方不会有,Picks博士可以据此测量出此时第 yy 个水箱的水渍高度,以推断曾经最多有多少水,节约他的建造成本。

现在,他请你来帮他做预实验,你能告诉他每次测量得到的电流大小以及测量得到的最多的水量是多少吗?

Solution

这题的转化对数据结构维护的东西的转化思想非常的机智。
主要问题就是max(a-x,0)
我们可以考虑维护一个max(a+x,b),考虑和max(x+c,d)合并一下
max(max(x+c,d)+a,b)=max(x+c+a,d+a,b)=max(x+c+a,max(d+a,b))
这样就又搞出来了一下max(x+a’,b’)的位置
然后我们只用考虑一下怎么求历史最大值
历史最大值其实只是求出所有的值得最大值
假如有一个max(x+a,b)和max(x+c,d),那么最大值就是max(max(x+a,b),max(x+c,d))
那么合并出来的也就是每一项的最大值,那么也可以用一个标记max(x+aa,bb)来维护,这个合并的时候就先合并一下历史的标记,然后在合并一下当前值得标记就好了。
只是要注意一下当加的比-inf还小的时候就可以不用加了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const ll maxn=5e5+7,inf=999999999999999;
ll i,j,k,l,n,m,o,r,x,y;
ll a[maxn];
struct node{
    ll a,b,da,db;
    void operator +=(node &x){
        da=max(da,a+x.da);db=max(db,max(b+x.da,x.db));
        a=max(a+x.a,-inf);b=max(b+x.a,x.b);
    }
}t[maxn*4],now;
void build(ll x,ll l,ll r){
    t[x]=(node){0,-inf,0,-inf};
    if(l==r){
        t[x].a=a[l];
        return;
    }
    ll mid=(l+r)/2;
    build(x*2,l,mid);build(x*2+1,mid+1,r);
}
void down(ll x){
    t[x*2]+=t[x];t[x*2+1]+=t[x];
    t[x]=(node){0,-inf,0,-inf};
}
void change(ll x,ll l,ll r,ll y,ll z){
    if(l==y&&r==z){t[x]+=now;return;}
    ll mid=(l+r)/2;
    down(x);
    if(z<=mid)change(x*2,l,mid,y,z);
    else if(y>mid)change(x*2+1,mid+1,r,y,z);
    else change(x*2,l,mid,y,mid),change(x*2+1,mid+1,r,mid+1,z);
}
void find(ll x,ll l,ll r,ll y){
    if(l==r){now=t[x];return;}
    down(x);ll mid=(l+r)/2;
    if(y<=mid)find(x*2,l,mid,y);else find(x*2+1,mid+1,r,y);
}
int main(){
//  freopen("fan.in","r",stdin);
    scanf("%lld%lld",&n,&m);
    fo(i,1,n)scanf("%lld",&a[i]);
    build(1,1,n);
    while(m--){
        scanf("%lld",&o);
        if(o<=3){
            scanf("%lld%lld%lld",&l,&r,&x);
            if(o==1)now=(node){x,-inf,x,-inf};
            else if(o==3)now=(node){-inf,x,-inf,x};
            else now=(node){-x,0,-x,0};
            change(1,1,n,l,r);
        }
        else{
            scanf("%lld",&x);
            find(1,1,n,x);
            if(o==4)printf("%lld\n",max(now.a,now.b));
            else printf("%lld\n",max(now.da,now.db));
        }
    }
}

你可能感兴趣的:(线段树)