poj3468 线段树的懒惰标记
题目链接: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 线段树的懒惰标记的更多相关文章
- HDU 4107 Gangster(线段树 特殊懒惰标记)
两种做法. 第一种:标记区间最大值和最小值,若区间最小值>=P,则本区间+2c,若区间最大值<P,则本区间+c.非常简单的区间更新. 最后发一点牢骚:最后query查一遍就行,我这个2B竟 ...
- HDU 3397 线段树 双懒惰标记
这个是去年遗留历史问题,之前思路混乱,搞了好多发都是WA,就没做了 自从上次做了大白书上那个双重懒惰标记的题目,做这个就思路很清晰了 跟上次大白上那个差不多,这个也是有一个sets标记,代表这个区间全 ...
- HDU 3954 Level up (线段树特殊懒惰标记)
http://blog.csdn.net/acm_cxlove/article/details/7548087 感觉最巧的是定义了min_dis……将区间内有无英雄升级分开处理 #include &l ...
- poj3468 线段树+lazy标记
A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 92921 ...
- POJ3468 线段树(区间更新,区间求和,延迟标记)
A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 97196 ...
- 线段树初步&&lazy标记
线段树 一.概述: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a, ...
- 【BZOJ-2892&1171】强袭作战&大sz的游戏 权值线段树+单调队列+标记永久化+DP
2892: 强袭作战 Time Limit: 50 Sec Memory Limit: 512 MBSubmit: 45 Solved: 30[Submit][Status][Discuss] D ...
- BZOJ 1798 (线段树||分块)的标记合并
我原来准备做方差的.. 结果发现不会维护两个标记.. 就是操作变成一个 a*x+b ,每次维护a , b 即可 加的时候a=1 ,b=v 乘的时候a=v ,b=0 #include <cstdi ...
- FZU 2171(线段树的延迟标记)
题意:容易理解. 分析:时隔很久,再一次写了一道线段树的代码,之前线段树的题也做了不少,包括各种延迟标记,但是在组队分任务之后,我们队的线段树就交给了另外一个队友在搞, 然后我就一直没去碰线段树的题了 ...
随机推荐
- C#实现CRC校验
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Web; ...
- c/c++ 图相关的函数(二维数组法)
c/c++ 图相关的函数(二维数组法) 遍历图 插入顶点 添加顶点间的线 删除顶点 删除顶点间的线 摧毁图 取得与v顶点有连线的第一个顶点 取得与v1顶点,v1顶点之后的v2顶点的之后的有连线的第一个 ...
- c/c++ 线性表之顺序表
线性表之顺序表 存储在连续的内存空间,和数组一样. 下面的代码,最开始定义了一个能存8个元素的顺序表,当超过8个元素的时候,会再追加开辟空间(函数:reInit). 实现了以下功能: 函数 功能描述 ...
- Linux下编译安装redis
redis是开源,BSD许可,高级的key-value存储系统,可以用来存储字符串,哈希结构,链表,集合,因此,常用来提供数据结构服务. redis和memcached相比的独特之处: 1: redi ...
- IntelliJ IDEA 创建maven管理的webapp项目
因为使用框架时基本需要使用maven管理项目,所以单独写一个搭建maven项目的流程 第一步: File-->New--Project 第二步: 选择maven框架 第三步: 输入工程id ...
- Scrapy 框架 分布式 爬虫
分布式 爬虫 scrapy-redis 实现 原生scrapy 无法实现 分布式 调度器和管道无法被分布式机群共享 环境安装 - pip install scrapy_redis 导包:from sc ...
- oracle windows 新建用户授权 导出导入bmp文件
1.新建用户 sqlplus /no log conn /as sysdba create user tkjj identified by tkjj2017 grant dba to tkjj con ...
- Integer判断大于 == 127时的坑
在一次判断返回Interger类型的code, 用==结果, 没进去 Integer的值在-128到127时,Integer对象是在IntegerCache.cache产生,会复用已有对象,也就是说 ...
- 用golang chromedp 操作已经打开的chrome浏览器
win7 环境,主要是一开始想在代码中先用exec.Command启动chrome,但始终不能成功监听9222端口,折腾了很长时间, 需要先手工启动chrome监听端口,具体写在代码注释中了. 然后再 ...
- Quartz 的DisallowConcurrentExecution
DisallowConcurrentExecution注解是指:一个JobKey对应的JobDetail实例不运行并发执行,而不是说你继承Job之后的子类不允许并发执行.