题目链接:poj3468

题意:给定一段数组,有两种操作,一种是给某段区间加c,另一种是查询一段区间的和

思路:暴力的方法是每次都给这段区间的点加c,查询也遍历一遍区间,复杂度是n*n,肯定过不去,另一种思路是用线段树记录区间的和,每次查询的复杂度是lgn,修改不必更新到每个点,当某个区间全被修改时,我们可以给它加一个懒惰标记,表示这个区间的所有下面节点都需要更新,只是因为现在不需要使用而暂时没有更新。这样修改的复杂度也降到了lgn

ac代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e5+10;
long long num[maxn],sum[maxn*4],lazy[maxn*4];
void pushdown(int rt,int len1,int len2)//向下更新懒惰标记
{
if(lazy[rt])
{
lazy[rt*2]+=lazy[rt];//注意是+=而不是=
lazy[rt*2+1]+=lazy[rt];
sum[rt*2]+=lazy[rt]*len1;
sum[rt*2+1]+=lazy[rt]*len2;
sum[rt]=sum[rt*2]+sum[rt*2+1];
lazy[rt]=0;
}
}
void build(int st,int en,int rt)
{
if(st==en)
{
sum[rt]=num[st];
return;
}
build(st,(st+en)/2,rt*2);
build((st+en)/2+1,en,rt*2+1);
sum[rt]=sum[rt*2]+sum[rt*2+1];
}
void add(int l,int r,int c,int st,int en,int rt)
{
int md=(st+en)/2;
if(l<=st&&r>=en)
{
lazy[rt]+=c;
sum[rt]+=(en-st+1)*c;
return ;
}
pushdown(rt,md-st+1,en-md);
if(r>=md+1)
{
add(l,r,c,md+1,en,rt*2+1);
}
if(l<=md)
{
add(l,r,c,st,md,rt*2);
}
sum[rt]=sum[rt*2]+sum[rt*2+1];
}
long long quer(int l,int r,int rt,int st,int en)
{
long long res=0,md=(st+en)/2;
if(l<=st&&r>=en)
return sum[rt];
pushdown(rt,md-st+1,en-md);
if(r>=md+1)
res+=quer(l,r,rt*2+1,md+1,en);
if(l<=md)
res+=quer(l,r,rt*2,st,md);
return res;
}
int main()
{
char comd;
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++)
scanf("%lld",&num[i]);
build(1,n,1);
for(int i=1;i<=q;i++)
{
int l,r,c;
cin>>comd;
if(comd=='C')
{
scanf("%d %d %d",&l,&r,&c);
add(l,r,c,1,n,1);
}
else
{
scanf("%d %d",&l,&r);
printf("%lld\n",quer(l,r,1,1,n));
}
}
return 0;
}

  

提升题:hdu6315

题意:有ab两个数组,有两种操作,一种是给a数组的一段区间加一,另一种是求a/b数组的累加和

思路:只有当一段区间最大的a大于最小的b时,这段区间的答案才会发生改变,如果没有发生改变,那么我们就不必要去给子区间修改最大a的值。我们给这段区间加个懒惰标记就可以了,以后浏览到这个区间时我们再修改。复杂度为nlgn

ac代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e5+10;
int sum[maxn*4],maxa[maxn*4],minb[maxn*4],b[maxn],lazy[maxn*4]; void pushup(int rt)
{
sum[rt]=sum[rt*2+1]+sum[rt*2];
maxa[rt]=max(maxa[rt*2],maxa[rt*2+1]);
minb[rt]=min(minb[rt*2],minb[rt*2+1]);
}
void pushdowm(int rt)
{
if(lazy[rt])
{
lazy[rt*2]+=lazy[rt];
lazy[rt*2+1]+=lazy[rt];
maxa[rt*2]+=lazy[rt];
maxa[rt*2+1]+=lazy[rt];
lazy[rt]=0;
}
}
void build(int st,int en,int rt)
{
if(st==en)
{
minb[rt]=b[st];
return;
}
build(st,(st+en)/2,rt*2);
build((st+en)/2+1,en,rt*2+1);
pushup(rt);
}
void add(int l,int r,int st,int en,int rt)
{
int md=(st+en)/2;
pushdowm(rt);
if(l<=st&&r>=en)
{
maxa[rt]++;
if(maxa[rt]>=minb[rt])
{
if(st!=en)
{
add(l,r,md+1,en,rt*2+1);
add(l,r,st,md,rt*2);
pushup(rt);
}
else
{
while(maxa[rt]>=minb[rt])
{
minb[rt]+=b[st];
sum[rt]++;
}
}
}
else
lazy[rt]++;
return;
}
else
{
if(l<=md)
add(l,r,st,md,rt*2);
if(r>=md+1)
add(l,r,md+1,en,rt*2+1);
}
pushup(rt);
}
int quer(int l,int r,int st,int en,int rt)
{
pushdowm(rt);
int md=(st+en)/2;
if(l<=st&&r>=en)
return sum[rt];
int res=0;
if(l<=md)
res+=quer(l,r,st,md,rt*2);
if(r>=md+1)
res+=quer(l,r,md+1,en,rt*2+1);
pushup(rt);
return res;
}
int main()
{
int n,q;
char comd[10];
while(cin>>n>>q)
{
for(int i=0;i<maxn*4;i++)sum[i]=0,maxa[i]=0,minb[i]=0,lazy[i]=0;
for(int i=0;i<maxn;i++)lazy[i]=0; for(int i=1; i<=n; i++)
scanf("%d",&b[i]);
build(1,n,1);
for(int i=1; i<=q; i++)
{
int l,r;
scanf("%s %d %d",&comd,&l,&r);
if(comd[0]=='a')
add(l,r,1,n,1);
else
printf("%d\n",quer(l,r,1,n,1));
}
}
return 0;
}

  

总结:懒惰标记可以解决一些区域修改问题

poj3468 线段树的懒惰标记的更多相关文章

  1. HDU 4107 Gangster(线段树 特殊懒惰标记)

    两种做法. 第一种:标记区间最大值和最小值,若区间最小值>=P,则本区间+2c,若区间最大值<P,则本区间+c.非常简单的区间更新. 最后发一点牢骚:最后query查一遍就行,我这个2B竟 ...

  2. HDU 3397 线段树 双懒惰标记

    这个是去年遗留历史问题,之前思路混乱,搞了好多发都是WA,就没做了 自从上次做了大白书上那个双重懒惰标记的题目,做这个就思路很清晰了 跟上次大白上那个差不多,这个也是有一个sets标记,代表这个区间全 ...

  3. HDU 3954 Level up (线段树特殊懒惰标记)

    http://blog.csdn.net/acm_cxlove/article/details/7548087 感觉最巧的是定义了min_dis……将区间内有无英雄升级分开处理 #include &l ...

  4. poj3468 线段树+lazy标记

    A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 92921   ...

  5. POJ3468 线段树(区间更新,区间求和,延迟标记)

    A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 97196   ...

  6. 线段树初步&&lazy标记

    线段树 一.概述: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a, ...

  7. 【BZOJ-2892&1171】强袭作战&大sz的游戏 权值线段树+单调队列+标记永久化+DP

    2892: 强袭作战 Time Limit: 50 Sec  Memory Limit: 512 MBSubmit: 45  Solved: 30[Submit][Status][Discuss] D ...

  8. BZOJ 1798 (线段树||分块)的标记合并

    我原来准备做方差的.. 结果发现不会维护两个标记.. 就是操作变成一个 a*x+b ,每次维护a , b 即可 加的时候a=1 ,b=v 乘的时候a=v ,b=0 #include <cstdi ...

  9. FZU 2171(线段树的延迟标记)

    题意:容易理解. 分析:时隔很久,再一次写了一道线段树的代码,之前线段树的题也做了不少,包括各种延迟标记,但是在组队分任务之后,我们队的线段树就交给了另外一个队友在搞, 然后我就一直没去碰线段树的题了 ...

随机推荐

  1. Cs231n课堂内容记录-Lecture2-Part1 图像分类

    Lecture 2 课程内容记录:(上)https://zhuanlan.zhihu.com/p/20894041?refer=intelligentunit (下)https://zhuanlan. ...

  2. [转]JIRA 7.2.6与Confluence 6.0.3的安装与配置之MS SQL Server版

    相关软件版本信息 说明:下方软件可以点击链接,通过百度云盘进行下载. 操作系统:Windows 10(密码:foht)或者Windows Server 2012(密码:lsad): 数据库:SQL S ...

  3. esxi网络中虚拟机的相关操作

    一个虚拟机的克隆就是原始虚拟机全部状态的一个备份或镜像.克隆的过程不影响原始虚拟机.而快照指的是虚拟磁盘在某一特定时间点的副本.执行快照将保留虚拟机的状况和数据. 一.实验拓扑图: 目标:克隆虚拟机, ...

  4. 【算法】LeetCode算法题-Length Of Last Word

    这是悦乐书的第155次更新,第157篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第14题(顺位题号是58).给定一个字符串,包含戴尔字母.小写字母和空格,返回最后一个单 ...

  5. Golang 并发简介

    并发概要 随着多核CPU的普及, 为了更快的处理任务, 出现了各种并发编程的模型, 主要有以下几种: 模型名称 优点 缺点 多进程 简单, 隔离性好, 进程间几乎无影响 开销最大 多线程 目前使用最多 ...

  6. 英语初级学习系列-00-Hello-打招呼

    开场白 当二咪过生日的时候,作为主人公(host)该如何开场呢? 1. Good evening, ladies and gentlemen. 先生们女士们,晚上好. 2. Welcome to my ...

  7. Socket实例

    一.socket处理单个连接 recv方法不是可以随便接收多大的数据都可以.官方建议是8KB,  即conn.recv(8192) import socket client = socket.sock ...

  8. 安装完最小化 RHEL/CentOS 7 后需要做的 30 件事情(一)转载自码农网

    CentOS 是一个工业标准的 Linux 发行版,是红帽企业版 Linux 的衍生版本.你安装完后马上就可以使用,但是为了更好地使用你的系统,你需要进行一些升级.安装新的软件包.配置特定服务和应用程 ...

  9. 关于IDE的选择

    以往忘了看到谁说的,说开发.NET就用VS,开发JS就用webStorm,开发java没钱用Eclipse,有钱用IDEA

  10. rem实现自适应

    总结一下,rem实现自适应:用rem代替px,配合媒体查询设置font-size:n%. 首先,px是死的.若一律用px,那就会大的显得小,小的屏幕显得大.其次,rem是活的,通过设置字体大小可以引起 ...