hihocoder-1080题解

时间:2023-01-28 12:39:16

一、题目链接

  http://hihocoder.com/problemset/problem/1080

二、题意

  一维区间,需要做区间增加和区间置值,以及对整个区间求和。

三、思路

  显然线段树是个利器。树状数组也可以支持快速区间增加和区间求和,而且编码也比线段树要短要简单(其实线段树也不难,越用越有味),但是对于区间置值,那就没辙了。

  中途插入一点心情小插曲:这题确实让我对带延迟标记的线段树理解了不少,历经各种bug的磨难终于AC后,对带延迟标记的线段树的理解确实更上一层楼了。写这个题的时候,先按照自己的理解写了一个版本,过了样例,交上去WA了,之后一直没明白为什么WA。现在终于明白了,感觉还是蛮爽的。第二个版本是昨晚参考题面上给的思路提示写的,当时也没想太多,写好后,过了样例又交了一发,还是WA,然后带着不明白睡了。今天起来后,继续调这个题目。搞了一个上午后,终于找出问题来了(虽然还是没有AC)。接下来进入正题。

  先说基本思路,再说本题最坑的地方。

  基本思路:线段树的每一个节点维护所表示区间的和值,这毫无疑问。 另外,由于这题需要支持区间增加和区间置值两种操作,所以,线段树每个节点需要两个延迟标记,add和set,两者都是int类型的,分别表示当前节点所表示的区间增加的值和设置的值(至于从什么时候开始累计增加的值,这个先不管,继续往下看)。如果没想到要定义两个延迟标记,参考题目的思路提示后也可知道需要这么干。否则,只记录区间和值搞不定啊。是吧。然后,update函数的形参中多写一个bool型参数,表示当前做的更新是增加还是置值。当当前处理的区间包含于要更新的区间时,根据刚刚那个bool型参数来决定是增加还是重新设置当前处理区间的和值。当当前处理的区间不完全包含于要更新的区间时,这时就需要对当前区间做延迟标记下推的工作。然后递归地对两个子节点区间做处理,再合并两个子节点的和值,作为当前节点维护的和值。这应该不难,都是正常带延迟标记的线段树的套路。接下来,关键问题来了。也是本题最有意思的地方来了。

  坑(最有趣的地方):下推函数怎么写?这个可以参考题目的思路提示写出来,但是,写出来之后样例是过了,交上去不对啊,下推函数左看右看上看下看都和提示一样,怎么就WA了呢?原因在于,提示中的下推函数没写全,它只给了我们一些思路提示,让我们知道要往那个方向去想,并没有完全把全过程写出来。接下来详细说说提示中下推函数的坑点。举个有错的样例: 


   -

  对于这个样例,第三次输出应该是1,但是完全按照提示中的下推函数来写,会输出-7。这明显就不对。原因在于,第一次把区间[1, 8]增加-4之后,区间[5, 6]也理所当然地增加了-4。第二次把区间[5, 6]设置为10,结果输出是-4也没错。第三次,输出结果就不对了。因为,第二次对区间[5, 6]置值之后,之前对[5, 6]的增加(也就是区间[5, 6]对应的节点的add值)值就没用了,应该设置为0,但是,思路提示中只是把它的子节点的add值设置成0而已。既然这样,那很简单,直接在下推函数中set下推的过程里面,把当前节点的add值设置为0就OK了。但是,如果这么干,又会带来这样的问题,还是举个反例:


  第四次输出的正确结果应该是94,但打了补丁后的程序输出却是91。插入一句题外话:线段树节点下标从1开始,1号节点对应的区间是[0, 9],2号节点对应的区间是[0, 4]。我们的补丁打的有问题,原因在于:第二次的更新中,2号节点(对应区间[0, 4])的set值被设为12。第三次更新中,2号节点(对应区间[0, 4])的add值被设为1,所以,2号节点的add != 0且set != -1。那么,在下推的过程中,先做置值,再做增加,在置值的过程中,由于我们加入“把当前节点的add值设为0”这么一行代码,导致下面的判增加的if代码块进不去,所以,当前节点的子节点没有获得区间置值后增加的值,导致结果不对。既然这样,那也好办,那就先做增加,再做置值。但是这样就更不对了,如果对于一个区间,第一次做了增加,还没下推。第二次又对这个区间置值。第三次对这个区间的子区间做更新,那么,下推这个区间的延迟标记时,因为是先做增加,再做置值,那么,第一次的add值就会下推给当前区间的子区间,导致结果偏大。

  加也不对,不加也不对,那不就没办法了么?确实,我当时也这么觉得。仔细分析第一种错误,你会发现,本来不该下推的add值被下推的主要原因是,当前区间被置值后,当前区间所对应的节点的add值没来得及置0。对于第二种错误,那是因为当前区间所对应的节点的add值置0置的太晚了,其实应该在区间被置值后立马就做的。既然这样,那我就把下推函数中判set的if代码块搬到update函数的区间置值完毕之后。这样,每次区间置值完毕,立马更新当前区间对应节点的add值为0,那么前两个问题不就愉快地解决了么?是的。就这样。然而,嘴上一分钟,嘴下十年功啊,我调了变天多才调出这么个法子来T_T。于是,最终AC的结果就如下所示了。

hihocoder-1080题解

四、源代码

  

#include<bits/stdc++.h>
using namespace std;
;
int n, m;
typedef struct {
    int add, set, sum;
} Node;
Node data[MAXN << ];

void init() {
    , t = MAXN << ; i < t; ++i) {
        data[i].add = ;
        data[i].sum = ;
        data[i].;
    }
}

void release_set(int root, int l, int r){
    ){//不是叶子节点
        , rch = root <<  | ;
        ;
        ) {
            data[lch].set = data[rch].set = data[root].set;
            data[lch].add = data[rch].add = ;
            data[lch].sum = data[root].);
            data[rch].sum = data[root].set * (r - mid);
            data[root].;
        }
    }
}

void release_add(int root, int l, int r) {
    ) {//不是叶子节点
        , rch = root <<  | ;
        ;
        ) {
            data[lch].add += data[root].add;
            data[rch].add += data[root].add;
            data[lch].sum += data[root].add * (mid - l + );
            data[rch].sum += data[root].add * (r - mid);
            data[root].add = ;
        }
    }
}

, , int r = n) {
    if(l > r)return;
    if(l > ur || r < ul)return;
    if(l >= ul && r <= ur) {
        if(isadd) {
            data[root].add += val;
            data[root].sum += val * (r - l + );
        } else {
            data[root].set = val;
            data[root].sum = val * (r - l + );
            release_set(root, l, r);
            data[root].add = ;//特别注意这句话
        }
        return;
    }
    release_set(root, l, r);//顺序不能乱。
    release_add(root, l, r);
    ;
    , l, mid);
     | , mid + , r);
    data[root].sum = data[root << ].sum + data[root <<  | ].sum;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input5.txt", "r", stdin);
    //freopen("output2.txt", "w", stdout);
#endif // ONLINE_JUDGE
    int a, b, c, d;
    while(~scanf("%d%d", &n, &m)) {
        init();
        ; i <= n; ++i) {
            scanf("%d", &a);
            update(i, i, false, a);
        }
        ; i < m; ++i) {
            scanf("%d%d%d%d", &a, &b, &c, &d);
            update(b, c, a == , d);
            printf(].sum);
            //for(int i = 1; i <= 19; ++i)printf("[%d](%d %d %d) ", i, data[i].add, data[i].set, data[i].sum); printf("\n\n");
            /*这是打出每个节点的信息,调试用的。*/
        }
    }
    ;
}

注:这是最终代码,中途经历很多个版本,很多种尝试,但那都不是关键,关键的地方也是本题最有意思的地方就在这里了。Last but not least,思路讲解有些地方可能讲的不够清楚明白,又或许有些地方语句不通顺或者有错别字,如果您还不明白,请在评论区留言,我们一起讨论。你懂得,博客这种东西,历经各种bug终于AC后感觉有很多话要说,一旦写起来,就不知道说些什么了。表达能力还有很大的提升空间。

hihocoder-1080题解的更多相关文章

  1. hihoCoder 1080 &colon; 更为复杂的买卖房屋姿势 线段树区间更新

    #1080 : 更为复杂的买卖房屋姿势 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho都是游戏迷,“模拟都市”是他们非常喜欢的一个游戏,在这个游戏里面他们 ...

  2. hihocoder 1080 线段树&lpar;区间更新&rpar;

    题目链接:http://hihocoder.com/problemset/problem/1080 , 两种操作的线段树(区间更新). 这道题前一段时间一直卡着我,当时也是基础不扎实做不出来,今天又想 ...

  3. hihocoder部分题解

    hihocoder1609 数组分拆II [dp] 给定数组,问有多少种拆法,使得每一段不出现重复的数字,且要保证分组数最少.(1e5) 题解: O(n) d[i]表示1~i最小划分的段数, f[i] ...

  4. hihoCoder&num;1080 (线段树)

    题目大意:线段树的区间更改与查询,但是涉及到两种区间修改方式,一是给区间中的数全部加上一个数,二是将一个区间全部置为同一个数,然后询问整个区间和. 题目分析:处理好set操作和add操作的先后顺序就O ...

  5. hihoCoder &num;1080 &colon; 更为复杂的买卖房屋姿势 &lpar;线段树,多tag&rpar;

    题意: 有编号为0~n的n+1个房屋,给出每个房屋的起始价格,随后给出m种修改,每次修改都要进行输出所有房屋的价格总和.修改有两种方式:(1)*调控,编号L~R全置为同一价格(0)房屋自行涨跌,编号 ...

  6. hihocoder &num;1419 &colon; 后缀数组四&&num;183&semi;重复旋律4

    #1419 : 后缀数组四·重复旋律4 时间限制:5000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为长度为 N 的数构 ...

  7. 【简要题解】Hihocoder 重复旋律1-9简要题解

    [简要题解]Hihocoder 重复旋律1-8简要题解 编号 名称标签 难度 1403 后缀数组一·重复旋律 Lv.4 1407 后缀数组二·重复旋律2 Lv.4 1415 后缀数组三·重复旋律3 L ...

  8. hihoCoder挑战赛14 A&comma;B&comma;C题解

    转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud 题目1 : 不等式 时间限制:10000ms 单点时限:1000ms 内存限制:2 ...

  9. HihoCoder 1636 Pangu and Stones(区间DP)题解

    题意:合并石子,每次只能合并l~r堆成1堆,代价是新石堆石子个数,问最后能不能合成1堆,不能输出0,能输出最小代价 思路:dp[l][r][t]表示把l到r的石堆合并成t需要的最小代价. 当t == ...

  10. HihoCoder 1634 Puzzle Game(最大子矩阵和)题解

    题意:给一个n*m的矩阵,你只能选择一个格子把这个格子的数换成p(也可以一个都不换),问最大子矩阵和最小可能是多少? 思路: 思路就是上面这个思路,这里简单讲一下怎么n^3求最大子矩阵和:枚举两行(或 ...

随机推荐

  1. 【转】理解inode

    From:http://www.ruanyifeng.com/blog/2011/12/inode.html  阮一峰大神真NB 作者: 阮一峰 日期: 2011年12月 4日 inode是一个重要概 ...

  2. 《DOM启蒙》 随笔

    使用 Javascript 字符串创建并向 DOM 中添加元素与文本节点 innerHTML.outerHTML.textContent 及 insertAdjacentHTML() 属性和方法提供了 ...

  3. 关于touch事件对于性能的影响

    第一次写博客随笔,废话不多说,直接进入正题. 最近一直专注于移动终端的开发,碰到了一个比较棘手的事情,就是touch事件,大家都知道,touch事件有几种,无非就是touchstart,touchmo ...

  4. 0c-38-ARC快速入门

    2.ARC快速使用 int main(){ Student *s = [[Student alloc] init]; return 0; } 只需要写一行代码,编译器会在合适的位置释放学生对象,程序员 ...

  5. Bone Collector&lpar;01背包&plus;记忆化搜索&rpar;

    Bone Collector Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Tota ...

  6. 【Python】Python的urllib模、urllib2模块的网络下载文件

    因为需要从一些下载一个页PDF文件.但是需要下载PDF有数百个文件,这是不可能用人工点击下载.只是Python有相关模块,所以写一个程序PDF文件下载,顺便熟悉Python的urllib模块和ulrl ...

  7. 【webpack】中file-loader和url-loader使用方法

    file-loader 可以指定要复制和放置资源文件的位置,以及如何使用版本哈希命名以获得更好的缓存.此外,这意味着 你可以就近管理图片文件,可以使用相对路径而不用担心部署时 URL 的问题.使用正确 ...

  8. Django进阶-auth集成认证模块

    auth认证模块是Django内置集成的一个用户认证模块. auth认证模块方法 方法 释义 auth.authenticate() 认证校验 auth.login(request,user) 封装认 ...

  9. HDU 5988 Coding Contest&lpar;最小费用最大流变形&rpar;

    Problem DescriptionA coding contest will be held in this university, in a huge playground. The whole ...

  10. Linux下FastDFS分布式存储-总结及部署记录

    一.分布式文件系统介绍分布式文件系统:Distributed file system, DFS,又叫做网络文件系统:Network File System.一种允许文件通过网络在多台主机上分享的文件系 ...