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(线段树的延迟标记)
题意:容易理解. 分析:时隔很久,再一次写了一道线段树的代码,之前线段树的题也做了不少,包括各种延迟标记,但是在组队分任务之后,我们队的线段树就交给了另外一个队友在搞, 然后我就一直没去碰线段树的题了 ...
随机推荐
- web前端(5)—— 常用标签2
以下三个不仅是常用标签了,还非常重要,所以请务必好好看,重要性从高到低: 盒模型div div标签是最常用最重要的,它可以把web页面分割成很多的小块分别管理 测试代码: <!DOCTYPE h ...
- 洗礼灵魂,修炼python(89)-- 知识拾遗篇 —— 进程
进程 1.含义:计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位.说白了就是一个程序的执行实例. 执行一个程序就是一个进程,比如你打开浏览器看到我的博客,浏览器本身是一 ...
- SQL Server 锁实验(INSERT加锁探究)
insert语句: 其上锁情况为: insert语句会对表上的所有索引作出更新,因此这里看到的索引列较多,我们先把所有的索引搞出来看看: 可以看到所有索引都涉及到了,然后我们来仔细分析下加锁情况: 1 ...
- 从零开始的cve分析- cve-2016-0095 简易记录
0x00 前言 看k0shl大佬的SSCTF pwn450 Windows Kernel Exploitation Writeup一文,试着写一个x64下的poc. poc地址:https://git ...
- 4.4Python数据处理篇之Matplotlib系列(四)---plt.bar()与plt.barh条形图
目录 目录 前言 (一)竖值条形图 (二)水平条形图 1.使用bar()绘制: 2.使用barh()绘制: (三)复杂的条形图 1.并列条形图: 2.叠加条形图: 3.添加图例于数据标签的条形图: 目 ...
- Nunit单元测试入门学习随笔(一)
Nunit单元测试 一.插件安装与项目关联 选择工具~扩展和更新 点击联机~搜索Nunit安装图内三个插件 新建单元测试项目 勾选项目引用 二.Nunit学习 1.了解单元测试 单元测试在我的理解是测 ...
- centos7下安装docker(22.docker swarm-----service)
运行service 执行以下命令: docker service create --name web-server httpd 通过docker service ls查看swarm中的service ...
- pip freeze 打包依赖库及setup.py
需要打包的工程目录下使用命令: pip freeze > requirements.txt 就会在pip目录生成 requirements.txt 文件,该文件内就是当前环境所安装的所有扩展包打 ...
- day1-pycharm使用
1.Ctrl+滑轮 字体大小 2.改变字体大小 3.开头模板 4.多行注释 ctrl+? 5.切换Python版本解释器
- Python 中的浅拷贝和深拷贝
1. 列表和字典,直接赋值,都是浅拷贝,即赋值双方指向同一地址,因为 Python 对可变对象按引用传递. >>> a = [1, 2, 3] >>> b = a ...