UVA11992----线段树的成段更新

题目地址:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=501&page=show_problem&problem=3143

这个题目的意思是

有一个矩阵,给你三个操作

分别是给一个子矩阵加一个值

或者将一个子矩阵的每个元素赋成一个值

再就是查询一个子矩阵的所有元素的和,最大值,最小值

因为是一个矩阵,我们可以针对每一行构造一个线段树

然后再对r行的结果再处理

所以这就是线段树的成段更新

那么这个线段树就有五个函数是很重要的

更新加的操作

更新赋值的操作

三个查询操作

其实难点主要在更新操作

因为既有赋值,又有加操作,所以弄清逻辑关系

如果当前区间有赋值标记,又有加标记,那显然是赋值标记优先的,因为你怎么加了,来个赋值就给你弄没了

想清楚这点很重要,然后在pushdown的时候,把两个标记都pushdown下去,并把孩子的sum,min,max处理,因为pushup的时候要用

pushup就很简单啦,直接网上pushup就OK

再就是pushdown的位置要弄清

如果当前更新区间已经符合要求,就没要pushdown了,直接update就好了,因为我们是对区间进行操作

如果没必要涉及到子区间,就别去动,容易出错。

在这里我要吐槽一下:UVA这么牛的网站怎么这么卡啊,还有这题的范围说的太宽泛了

最多有10^6个元素,最有20行,也就是说一行最多可以10^6个元素啊,尼玛*4就爆了啊

强烈吐槽

下面是我的代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

const int maxn = 20+5;
struct node
{
    int sum;
    int mins;
    int maxs;
    int add;
    int set;
};

node tree[maxn][1<<17];

int r,c,m;

void build(int num,int l,int r,int rt)
{
    tree[num][rt].add=tree[num][rt].maxs = tree[num][rt].mins = tree[num][rt].sum = 0;
    tree[num][rt].set = -1;
    if(l==r)
        return;
    int m = (l+r)>>1;
    build(num,lson);
    build(num,rson);
}

void pushdown(int num,int l,int r,int rt)
{
    int m=(l+r)>>1;
    if(tree[num][rt].set>=0)
    {
        tree[num][rt<<1].set = tree[num][rt<<1|1].set=tree[num][rt].set;
        tree[num][rt<<1].add = tree[num][rt<<1|1].add=0;
        tree[num][rt<<1].mins = tree[num][rt<<1].maxs = tree[num][rt].set;
        tree[num][rt<<1|1].maxs = tree[num][rt<<1|1].mins = tree[num][rt].set;
        tree[num][rt<<1].sum = (m-l+1)*tree[num][rt].set;
        tree[num][rt<<1|1].sum = (r-m)*tree[num][rt].set;
        tree[num][rt].set=-1;
    }
    if(tree[num][rt].add>0)
    {
        tree[num][rt<<1].add +=tree[num][rt].add;
        tree[num][rt<<1|1].add +=tree[num][rt].add;
        tree[num][rt<<1].mins+=tree[num][rt].add;
        tree[num][rt<<1].maxs+=tree[num][rt].add;
        tree[num][rt<<1|1].maxs+=tree[num][rt].add;
        tree[num][rt<<1|1].mins+=tree[num][rt].add;
        tree[num][rt<<1].sum+=(m-l+1)*tree[num][rt].add;
        tree[num][rt<<1|1].sum+=(r-m)*tree[num][rt].add;
        tree[num][rt].add=0;
    }
}

void pushup(int num,int rt)
{
    tree[num][rt].sum = tree[num][rt<<1].sum+tree[num][rt<<1|1].sum;
    tree[num][rt].maxs = max(tree[num][rt<<1].maxs,tree[num][rt<<1|1].maxs);
    tree[num][rt].mins = min(tree[num][rt<<1].mins,tree[num][rt<<1|1].mins);
}

void update_add(int num,int l,int r,int rt,int L,int R,int v)
{
    if(L<=l && r<=R)
    {
        tree[num][rt].sum+=(r-l+1)*v;
        tree[num][rt].mins+=v;
        tree[num][rt].maxs+=v;
        tree[num][rt].add+=v;
        return;
    }
    pushdown(num,l,r,rt);
    int m = (l+r)>>1;
    if(L<=m)
        update_add(num,lson,L,R,v);
    if(R>m)
        update_add(num,rson,L,R,v);
    pushup(num,rt);
}

void update_set(int num,int l,int r,int rt,int L,int R,int v)
{
    if(L<=l && r<=R)
    {
        tree[num][rt].sum=(r-l+1)*v;
        tree[num][rt].mins=v;
        tree[num][rt].maxs=v;
        tree[num][rt].add=0;
        tree[num][rt].set=v;
        return;
    }
    //这里一定要不是查询区间了再往下推,而不能直接往下推,即pushdown不能放到前面去
    pushdown(num,l,r,rt);
    int m = (l+r)>>1;
    if(L<=m)
        update_set(num,lson,L,R,v);
    if(R>m)
        update_set(num,rson,L,R,v);
    pushup(num,rt);
}

int query_sum(int num,int l,int r,int rt,int L,int R)
{
    if(L<=l && r<=R)
        return tree[num][rt].sum;
    pushdown(num,l,r,rt);
    int ret = 0;
    int m = (l+r)>>1;
    if(L<=m)
        ret+=query_sum(num,lson,L,R);
    if(R>m)
        ret+=query_sum(num,rson,L,R);
    pushup(num,rt);
    return ret;
}

int query_min(int num,int l,int r,int rt,int L,int R)
{
    if(L<=l && r<=R)
        return tree[num][rt].mins;
    pushdown(num,l,r,rt);
    int ret = 1000000000;
    int m = (l+r)>>1;
    if(L<=m)
        ret = min(ret,query_min(num,lson,L,R));
    if(R>m)
        ret = min(ret,query_min(num,rson,L,R));
    pushup(num,rt);
    return ret;
}

int query_max(int num,int l,int r,int rt,int L,int R)
{
    if(L<=l && r<=R)
        return tree[num][rt].maxs;
    pushdown(num,l,r,rt);
    int ret = -1000000000;
    int m = (l+r)>>1;
    if(L<=m)
        ret = max(ret,query_max(num,lson,L,R));
    if(R>m)
        ret = max(ret,query_max(num,rson,L,R));
    pushup(num,rt);
    return ret;
}

int main()
{
    while(~scanf("%d%d%d",&r,&c,&m))
    {
        for(int i=1;i<=r;i++)
            build(i,1,c,1);
        int q,x1,y1,x2,y2,v;
        while(m--)
        {
            int asum=0;
            int amax = -1000000000;
            int amin = 1000000000;
            scanf("%d%d%d%d%d",&q,&x1,&y1,&x2,&y2);
            if(q!=3)
                scanf("%d",&v);
            if(q==1)
            {
                //printf("q1\n");
                for(int i=x1;i<=x2;i++)//更新x个树
                {
                    update_add(i,1,c,1,y1,y2,v);
                }
                //printf("q1 done\n");
            }
            else if(q==2)
            {
                for(int i=x1;i<=x2;i++)//更新x个树
                {
                    update_set(i,1,c,1,y1,y2,v);
                }
            }
            else
            {
                //printf("q\n");
                for(int i=x1;i<=x2;i++)
                {
                    asum+=query_sum(i,1,c,1,y1,y2);
                    //printf("q s\n");
                    int t1 = query_min(i,1,c,1,y1,y2);
                    //printf("q min\n");
                    int t2 = query_max(i,1,c,1,y1,y2);
                    //printf("q max\n");
                    if(amin>t1)
                        amin=t1;
                    if(amax<t2)
                        amax=t2;
                }
                printf("%d %d %d\n",asum,amin,amax);
            }

        }
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值