您的位置:首页 > 其它

文章标题 HihoCoder - 1080 : 更为复杂的买卖房屋姿势(线段树)

2017-07-19 16:17 323 查看

更为复杂的买卖房屋姿势

描述

小Hi和小Ho都是游戏迷,“模拟都市”是他们非常喜欢的一个游戏,在这个游戏里面他们可以化身上帝模式,买卖房产。

在这个游戏里,会不断的发生如下两种事件:一种是房屋自发的涨价或者降价,而另一种是政府有关部门针对房价的硬性调控。房价的变化自然影响到小Hi和小Ho的决策,所以他们希望能够知道任意时刻某个街道中所有房屋的房价总和是多少——但是很不幸的,游戏本身并不提供这样的计算。不过这难不倒小Hi和小Ho,他们将这个问题抽象了一下,成为了这样的问题:

小Hi和小Ho所关注的街道的长度为N米,从一端开始每隔1米就有一栋房屋,依次编号为0..N,在游戏的最开始,每栋房屋都有一个初始价格,其中编号为i的房屋的初始价格为p_i,之后共计发生了M次事件,所有的事件都是对于编号连续的一些房屋发生的,其中第i次事件如果是房屋自发的涨价或者降价,则被描述为三元组(L_i, R_i, D_i),表示编号在[L_i, R_i]范围内的房屋的价格的增量(即正数为涨价,负数为降价)为D_i;如果是政府有关部门针对房价的硬性调控,则被描述为三元组(L_i, R_i, V_i),表示编号在[L_i, R_i]范围内的房屋的价格全部变为V_i。而小Hi和小Ho希望知道的是——每次事件发生之后,这个街道中所有房屋的房价总和是多少。

提示:这是练习向的一周~

× Close


提示:这是练习向的一周~

小Hi道:“小Ho,最近你也学了很多线段树的知识了,而现在我们遇到的这个问题其实也就是之前问题略微复杂化的版本,你有什么想法么?”

小Ho道:“唔……查询仍然是很简单,很明显我们可以知道维护的值是区间和,但是问题在于有两种修改操作——并且都是区间修改,也就是说如果我使用懒惰标记来维护修改的话,我就只需要处理好这两种操作之间的冲突咯?简单!”

“既然你说简单,那你就去试试好咯?~”小Hi笑道。

于是半天后,小Ho晕头转向的回来了:“不行啊,分支情况太多,整理不清楚。”

“那我就跟你讲个法子,首先你要弄清楚每个节点维护的值是标记时立即更新还是标记释放之后再更新。”小Hi道。

“这……一般标记打上之后都会马上用它的值更新父亲节点,所以应该是立即更新的吧!”小Ho想了想道。

“所以你可以把下放标记作为一个单独的方法拎出来,然后每次访问到一个节点的时候,不管三七二十一先执行了这个方法先。”小Hi写了写,递给了小Ho。



“这样的话,我每次进行新的修改操作的时候,都可以确保当前节点上没有任何的懒惰标记……那不就很简单了!”小Ho点了点头。

“先等你通过了再说简单吧!”小Ho嘿嘿笑道。

Close


输入

每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第1行为两个整数N、M,分别表示街道的长度和总共发生的事件数。

每组测试数据的第2行为N+1个整数,其中第i个整数位p_i,表示编号为i的房屋的初始价格。

每组测试数据的第3-M+2行,按照发生的时间顺序,每行描述一个事件,如果该行描述的事件为,“房屋自发的涨价或者降价”,则该行为4个整数0, L_i, R_i, D_i,意义如前文所述;如果该行描述的事件为“政府有关部门针对房价的硬性调控”,则该行为4个整数1, L_i, R_i, V_i,意义如前文所述。

对于100%的数据,满足N<=10^5,1<=p_i, |D_i|, V_i<=10^4,0<=l_i

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;

const int maxn = 100050<<2;

int sum[maxn];
int setv[maxn];
int add[maxn];

int n,m;

void push_up(int rt){//向上更新
sum[rt]=sum[rt*2]+sum[rt*2+1];
}

void push_down(int rt,int len){//向下更新
if (setv[rt]){//如果存在懒惰标记
setv[rt*2]=setv[rt*2+1]=setv[rt];//下放懒惰标记
add[rt*2]=add[rt*2+1]=0;//懒惰标记覆盖add标记
//重新维护节点的值
sum[rt*2]=(len-len/2)*setv[rt];
sum[rt*2+1]=(len/2)*setv[rt];
//移除懒惰标记
setv[rt]=0;
}
if (add[rt]!=0){//如果存在add标记
//下放add标记
add[rt*2]+=add[rt];
add[rt*2+1]+=add[rt];
//从新计算节点的值
sum[rt*2]+=(len-len/2)*add[rt];
sum[rt*2+1]+=(len/2)*add[rt];
//移除add 标记
add[rt]=0;
}

}

void build (int rt,int l,int r){//建树
if (l==r){
scanf ("%d",&sum[rt]);
return ;
}
int mid=(l+r)/2;
build (rt*2,l,mid);
build (rt*2+1,mid+1,r);
push_up(rt);
}

void update(int rt,int l,int r,int ul,int ur,int val,int op){
if (l>=ul&&r<=ur){
if (op){//1 操作
setv[rt]=val;
sum[rt]=(r-l+1)*val;
add[rt]=0;
}else {//0 操作
add[rt]+=val;
sum[rt]+=(r-l+1)*val;
}
return;
}
push_down(rt,r-l+1);
int mid=(r+l)/2;
if (ul<=mid){
update(rt*2,l,mid,ul,ur,val,op);
}
if (ur>mid){
update(rt*2+1,mid+1,r,ul,ur,val,op);
}
push_up(rt);
}

int main()
{
scanf ("%d%d",&n,&m);
memset (setv,0,sizeof (setv));
memset (add,0,sizeof (add));
build(1,0,n);
int op,ul,ur,val;
while (m--){
scanf ("%d%d%d%d",&op,&ul,&ur,&val);
update(1,0,n,ul,ur,val,op);
printf ("%d\n",sum[1]);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: