[UOJ 25][IOI2014]Wall(裸线段树)

题目链接

http://uoj.ac/problem/25

题目思路

其实这个题在ACM/ICPC Shanghai Training Camp的比赛中也有见过,当时赛题中还要求叠加一个区间加标记,在我的对拍查错的配合下,我们队的sol神犇当时A掉了那个题。
这个题很显然比上海的赛题简单很多对吧这里写图片描述,不过我还是太弱了,加之第一次做交互题,所以花了一上午才AC。。。。
注意到应用在序列上的操作可以总结为”如果它的初值( val ) max_tag,max_tagmin_tag,min_tag ”的形式
我们不妨设区间中元素 i 的值为val[i],max{max_tag[i],min{min_tag[i],val[i]}}(注意下取max和min的顺序,不过好像先取后取都无所谓),我们的线段树需要支持以下操作:
1. SegmentMax(L,R,h):[L,R]max{h}
2. SegmentMin(L,R,h):[L,R]min{h}
可以转换成
1. updateMax(o,h):线omax{h}
2. updateMin(o,h):线omin{h}
对于(1)而言,若 h<max_tag[o],max_tag[o]=hh<min_tag[o],min_tag[o]=h ,这个的正确性自己脑补下就行了。
对于(2)也是如此。
于是此题就做出来了

代码

#include "wall.h"

const int MAXN=8000010;
const int MAXH=100000;

int max_tag[MAXN],min_tag[MAXN],val[MAXN];

void updateMax(int o,int h) //修改结点o的max标记为h
{
    if(h>max_tag[o]) max_tag[o]=h;
    if(h>min_tag[o]) min_tag[o]=h;
}

void updateMin(int o,int h) //修改结点o的min标记为h
{
    if(h<max_tag[o]) max_tag[o]=h;
    if(h<min_tag[o]) min_tag[o]=h;
}

void pushdown(int o) //下传标记操作
{
    updateMax(o<<1,max_tag[o]);
    updateMax(o<<1|1,max_tag[o]);
    max_tag[o]=0;
    updateMin(o<<1,min_tag[o]);
    updateMin(o<<1|1,min_tag[o]);
    min_tag[o]=MAXH;
}

void build(int o,int L,int R)
{
    if(L==R)
    {
        max_tag[o]=min_tag[o]=val[o]=0;
        return;
    }
    int M=(L+R)>>1;
    max_tag[o]=0;
    min_tag[o]=MAXH;
    val[o]=0;
    build(o<<1,L,M);
    build(o<<1|1,M+1,R);
}

void changeMax(int o,int L,int R,int ql,int qr,int h) //[ql,qr]区间取max
{
    if(ql<=L&&R<=qr)
    {
        updateMax(o,h);
        return;
    }
    pushdown(o);
    int M=(L+R)>>1;
    if(ql<=M)
        changeMax(o<<1,L,M,ql,qr,h);
    if(M<qr)
        changeMax(o<<1|1,M+1,R,ql,qr,h);
}

void changeMin(int o,int L,int R,int ql,int qr,int h) //[ql,qr]区间取min
{
    if(ql<=L&&R<=qr)
    {
        updateMin(o,h);
        return;
    }
    pushdown(o);
    int M=(L+R)>>1;
    if(ql<=M)
        changeMin(o<<1,L,M,ql,qr,h);
    if(M<qr)
        changeMin(o<<1|1,M+1,R,ql,qr,h);
}

void writeans(int o,int L,int R,int *&output)
{
    if(L==R)
        *output++=max_tag[o];
    else
    {
        pushdown(o);
        int M=(L+R)>>1;
        writeans(o<<1,L,M,output);
        writeans(o<<1|1,M+1,R,output);
    }
}

void buildWall(int n,int k,int op[],int left[],int right[],int height[],int finalHeight[])
{
    build(1,1,n);
    for(int i=0;i<k;i++)
    {
        if(op[i]==1) //区间取max
            changeMax(1,1,n,left[i]+1,right[i]+1,height[i]);
        else //区间取min
            changeMin(1,1,n,left[i]+1,right[i]+1,height[i]);
    }
    int *output=finalHeight;
    writeans(1,1,n,output);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值