美文网首页线段树
紧急通知!发现一颗优秀的线段树能卡过的题!!

紧急通知!发现一颗优秀的线段树能卡过的题!!

作者: A黄橙橙 | 来源:发表于2018-03-29 01:21 被阅读0次

UVA - 11992
一道丧心病狂的题。
题意:给你一个二维矩阵,初始矩阵中数字全为0,保证数字个数少于1e6,现在有三种操作:1.把一个子矩阵中的数字全部增加v,2.把一个子矩阵中的数字全部变成v,3.查询子矩阵的和,最大值和最小值。
分析:本题的难点有两个。一,这是一个二维矩阵;二,涉及到一个增加操作和一个变换操作。对于第一个难点,因为它保证了数字个数少于1e6,所以我们可以把这个矩阵变成一个行向量,这样在查询的时候我们只需要分段查询即可(求和相加,最值做全局变量)。对于第二个难题,其实在HihoCoder - 1080已经遇到过。

#include<bits/stdc++.h>

#define ll long long

const int INF = 0x3f3f3f3f;

const int maxn = 1e6+100;

using namespace std;

struct node{
    int add;
    int ct;
    int Sum;
    int Max;
    int Min;
    int l,r;
}tree[maxn<<2];

int Min,Max;

void pushDown(int rt){
    if(tree[rt].ct!=-1){      //②
        tree[rt<<1].add=0;
        tree[rt<<1].Sum=(tree[rt<<1].r-tree[rt<<1].l+1)*tree[rt].ct;
        tree[rt<<1].Max=tree[rt].ct;
        tree[rt<<1].Min=tree[rt].ct;
        tree[rt<<1].ct=tree[rt].ct;
        tree[rt<<1|1].add=0;
        tree[rt<<1|1].Sum=(tree[rt<<1|1].r-tree[rt<<1|1].l+1)*tree[rt].ct;
        tree[rt<<1|1].Max=tree[rt].ct;
        tree[rt<<1|1].Min=tree[rt].ct;
        tree[rt<<1|1].ct=tree[rt].ct;
        tree[rt].ct=-1;
    }
    if(tree[rt].add){
        tree[rt<<1].add+=tree[rt].add;
        tree[rt<<1].Sum+=(tree[rt<<1].r-tree[rt<<1].l+1)*tree[rt].add;
        tree[rt<<1].Max+=tree[rt].add;
        tree[rt<<1].Min+=tree[rt].add;
        tree[rt<<1|1].Sum+=(tree[rt<<1|1].r-tree[rt<<1|1].l+1)*tree[rt].add;
        tree[rt<<1|1].Max+=tree[rt].add;
        tree[rt<<1|1].Min+=tree[rt].add;
        tree[rt<<1|1].add+=tree[rt].add;
        tree[rt].add=0;
    }
}

void pushUp(int rt){
    tree[rt].Sum=tree[rt<<1].Sum+tree[rt<<1|1].Sum;
    tree[rt].Max=max(tree[rt<<1].Max,tree[rt<<1|1].Max);
    tree[rt].Min=min(tree[rt<<1].Min,tree[rt<<1|1].Min);
}

void Updata(int rt,int l,int r,int v,int op){
    if(tree[rt].l >= l && tree[rt].r <= r){
        if(op==1){
            tree[rt].add+=v;
            tree[rt].Sum+=v*(tree[rt].r-tree[rt].l+1);
            tree[rt].Min+=v;
            tree[rt].Max+=v;
        }else{
            tree[rt].add=0;      //①
            tree[rt].ct=v;
            tree[rt].Sum=v*(tree[rt].r-tree[rt].l+1);
            tree[rt].Min=v;
            tree[rt].Max=v;
        }
        return;
    }
    pushDown(rt);
    if(tree[rt<<1].r>=l){
        Updata(rt<<1,l,r,v,op);
    }
    if(tree[rt<<1|1].l<=r){
        Updata(rt<<1|1,l,r,v,op);
    }
    pushUp(rt);
}

int Query(int rt,int l,int r)
{
    if(tree[rt].l==l && tree[rt].r==r){
        Min=min(Min,tree[rt].Min);
        Max=max(Max,tree[rt].Max);
        return tree[rt].Sum;
    }
    pushDown(rt);
    if(tree[rt<<1|1].l<=l){
        return Query(rt<<1|1,l,r);
    }else if(tree[rt<<1].r>=r){
        return Query(rt<<1,l,r);
    }else{
        return Query(rt<<1|1,tree[rt<<1|1].l,r)+Query(rt<<1,l,tree[rt<<1].r);
    }
}

void Creat(int rt,int l,int r){
    tree[rt].l=l;
    tree[rt].r=r;
    tree[rt].add=0;
    tree[rt].ct=-1;
    tree[rt].Sum=0;
    tree[rt].Max=0;
    tree[rt].Min=0;
    if(l==r){
        return;
    }
    int mid=(l+r)>>1;
    Creat(rt<<1,l,mid);
    Creat(rt<<1|1,mid+1,r);
}

void Solve()
{
    int r,c,m;
    while(scanf("%d%d%d",&r,&c,&m)!=EOF){
    Creat(1,1,r*c);
    int op,x1,y1,x2,y2,v;
    for(int i=0;i<m;i++){
        scanf("%d%d%d%d%d",&op,&x1,&y1,&x2,&y2);
        if(op==1){
            scanf("%d",&v);
            for(int i=x1;i<=x2;i++){
                Updata(1,(i-1)*c+y1,(i-1)*c+y2,v,op);
            }
        }else if(op==2){
            scanf("%d",&v);
            for(int i=x1;i<=x2;i++){
                Updata(1,(i-1)*c+y1,(i-1)*c+y2,v,op);
            }
        }else{
            int Sum=0;
            Min=INF, Max=0;
            for(int i=x1;i<=x2;i++)
                Sum+=Query(1,(i-1)*c+y1,(i-1)*c+y2);
            printf("%d %d %d\n",Sum,Min,Max);
        }
    }
    }
}

int main()
{
    //FILEIN
    //FILEOUT
    //std::ios::sync_with_stdio(false);
    int Case=1,cases;
    //scanf("%d", &Case); cases=Case;
    while(Case--){
        //printf("Case #%d:",cases-Case);
        Solve();
    }
    return 0;
}

这是一颗优秀的线段树,只跑300ms的线段树!

在解决第一个问题之后,其实写法就是常规的线段树写法,跑300ms的原因是在Creat()的时候我并没有调用pushUp()。

写在最后的话主要是想说一下开始WA的几个点。
其实就是对于已经有add并且没有pushDown的点,在接受到了ct(change_to)之后怎么处理的问题。
注释1. 在接受了ct之后,应该把原来有的add消去。
注释2.这两个if语句顺序都不能换!(虽然不知道为什么,感觉换了也仍然会因为ct!=-1而变回去)

还有一点,常规的线段树的题,无非就是求和及最值,在本题中对于不同的问题时函数的写法都有表现了。

相关文章

  • 紧急通知!发现一颗优秀的线段树能卡过的题!!

    UVA - 11992一道丧心病狂的题。题意:给你一个二维矩阵,初始矩阵中数字全为0,保证数字个数少于1e6,现在...

  • 数据结构-线段树

    实现一个线段树 下面实现的线段树,有三个功能: 把数组构建成一颗线段树 线段树的修改 线段树的查询 303号问题 ...

  • 卧槽卧槽卧槽

    好险还好爸爸码力深厚莽过去了吓死爸爸了我爱线段树不然怕是要卡题卡一下午哦

  • HDU1540(Tunnel Warfare )

    链接:https://vjudge.net/problem/HDU-1540思路:这个题真的想透我感觉能明白线段树...

  • Java 算法 - 约翰的生意(线段树)

    题意 样例 1.解题思路   这是一道非常典型的线段树题。之前我也做过类似的题,Java 算法-区间求和I(线段树...

  • Java 算法-区间求和I(线段树)

      我之前学过线段树,掌握的不是很牢固,而这道题是线段树,为了加深记忆,所以还是觉得有必要记录下来,就当是对线段树...

  • 线段树

    昨天画了个把小时把lintcode上跟线段树的几道题用java做了一下。把线段树相关的几题都Accepted了。。...

  • Java 算法 - 进程序列

    题意 样例 1.解题思路   说实话,才开始我的思路是:使用线段树来构造,因为这个题符合的线段树的特性,但是写的时...

  • 线段树区间修改模板

    对应的水题是poj3468 今天实验室的大牛说了线段树的区间修改值在求和,(其实自己线段树还没懂太多了)觉得他们好...

  • 牛客练习赛28(数据结构)

    https://www.nowcoder.com/acm/contest/200/B思路:比较经典的线段树模板题,...

网友评论

    本文标题:紧急通知!发现一颗优秀的线段树能卡过的题!!

    本文链接:https://www.haomeiwen.com/subject/zyupcftx.html