[bzoj5050][线段树]建造摩天楼
Description
属于小Q管辖的n座摩天楼从左往右排成一排,编号依次为1到n,第i座摩天楼的高度为h_i。小Q会进行m次以下两种 操作之一: 1 l
r,询问h_l+h_{l+1}+…+h_r。 2 l
r,对于第l到r的每座摩天楼i,如果上次操作结束时h_i<h_{i-1},则将第i座摩天楼再往上造一层,即h_i增加1。
你可以认为h_0=正无穷。 请写一个程序回答小Q的每个询问。注意,此题操作一和操作二弄反了
Input
第一行包含两个正整数n,m(1<=n<=100000,1<=m<=min(100000,2n)),分别表示摩天楼的数量以及操作的数量。
第二行包含n个正整数h_1,h_2,…,h_n(1<=h_i<=n),表示每座楼的高度。
接下来m行,每行三个正整数op,l,r(1<=op<=2,1<=l<=r<=n),分别表示每个操作。
因为小Q觉得错乱不齐的建筑更加美观,因此你可以认为数据是完全随机的。
Output
对于每个询问输出一行一个整数,即区间内所有摩天楼的高度之和。
Sample Input
5 8
1 3 2 2 4
1 2 4
2 2 2
2 3 3
2 4 4
1 1 3
2 1 1
2 2 2
2 3 3
Sample Output
3
3
2
2
3
3
题解
观察到这个h是随机的
于是可以大胆猜想一种关于差值的算法
一开始觉得可以分块,每个块维护块内和前面差值最小的,加就直接加然后差值变为0了就得暴力重构
然而分块这样会出现一些问题,大概是差值变为0了之后每个块的重构次数就会暴涨
然后这样就不能分析了
而且感觉代码也超级难写的样子,还要讨论块前一个的贡献
可以把分块改成线段树,同样维护区间差值最小
单考虑一个点的话,有两种东西要维护
如果他比左边的点要矮,你需要维护一个值使得加了这个值的次数后这个点就不增了
如果他比右边的点要矮,同样要维护一个值使得加了这个值的次数后右边的点就要变得增了
注意这里记录的值都是在处于左边/右边的点不动的情况下讨论的,因为动的话差值并不会缩小
于是可以直接维护了,加就直接加
每次找到差值为0的地方,把这个点拿出来暴力重构他,以及他左边的点的差值和右边点的差值
注意并不能重构左右的点,因为重构左右的点的话可能会使得左右的点下一次就不能被增加了,但是实际上是可以被增加的,因为左右的左右的点还没有把标记下传
具体的话…可以看看代码
有注释感觉挺好懂的
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
#define lc now<<1
#define rc now<<1|1
using namespace std;
inline int read()
{int f=1,x=0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;
}
int stack[20];
inline void write(LL x)
{if(x<0){putchar('-');x=-x;}if(!x){putchar('0');return;}int top=0;while(x)stack[++top]=x%10,x/=10;while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=100005;int a[MAXN],n,m;
int ok[MAXN],L[MAXN],R[MAXN];//这个点是否可以增加 左边是否要注意重构 右边是否要注意重构
bool f1(int x){return a[x-1]>a[x];}//这个点是否可以增加
int cl(int x){return (ok[x]&&!ok[x-1])?a[x-1]-a[x]:999999999;}
int cr(int x){return (ok[x]&&!ok[x+1])?a[x+1]-a[x]:999999999;}int list[MAXN],tp;//需要重构的点 struct segtree
{LL sum[4*MAXN];int size[4*MAXN],add[4*MAXN],minl[4*MAXN],minr[4*MAXN];//有多少个可用点 void pushup(int now){size[now]=size[lc]+size[rc];sum[now]=sum[lc]+sum[rc];minl[now]=min(minl[lc],minl[rc]);minr[now]=min(minr[lc],minr[rc]);}void pushdown(int now){if(!add[now])return ;sum[lc]+=add[now]*size[lc];sum[rc]+=add[now]*size[rc];add[lc]+=add[now];add[rc]+=add[now];minl[lc]-=add[now];minl[rc]-=add[now];minr[lc]-=add[now];minr[rc]-=add[now];add[now]=0;}void buildtree(int now,int l,int r){if(l==r){size[now]=ok[l];sum[now]=a[l];minl[now]=L[l];minr[now]=R[l];return ;}int mid=(l+r)/2;buildtree(lc,l,mid);buildtree(rc,mid+1,r);pushup(now);}LL qry(int now,int l,int r,int ql,int qr){if(l==ql&&r==qr)return sum[now];int mid=(l+r)/2;pushdown(now);if(qr<=mid)return qry(lc,l,mid,ql,qr);else if(mid+1<=ql)return qry(rc,mid+1,r,ql,qr);else return qry(lc,l,mid,ql,mid)+qry(rc,mid+1,r,mid+1,qr);}void newcal(int now,int l,int r,int p)//得到a[p]新的值{if(l==r){if(ok[l])a[l]+=add[now];add[now]=0;return ;}int mid=(l+r)/2;pushdown(now);if(p<=mid)newcal(lc,l,mid,p);else newcal(rc,mid+1,r,p);}void modify(int now,int l,int r,int ql,int qr){if(l==ql&&r==qr){sum[now]+=size[now];add[now]++;minl[now]--;minr[now]--;return ;}int mid=(l+r)/2;pushdown(now);if(qr<=mid)modify(lc,l,mid,ql,qr);else if(mid+1<=ql)modify(rc,mid+1,r,ql,qr);else modify(lc,l,mid,ql,mid),modify(rc,mid+1,r,mid+1,qr);pushup(now);}void work(int now,int l,int r,int p)//把p的线段树节点全部更换 {if(l==r){sum[now]=a[l];size[now]=ok[l];minl[now]=L[l];minr[now]=R[l];return ;}int mid=(l+r)/2;pushdown(now);if(p<=mid)work(lc,l,mid,p);else work(rc,mid+1,r,p);pushup(now);}void putl(int now,int l,int r,int p,int c){if(l==r){minl[now]=c;return ;}int mid=(l+r)/2;pushdown(now);if(p<=mid)putl(lc,l,mid,p,c);else putl(rc,mid+1,r,p,c);minl[now]=min(minl[lc],minl[rc]);}void putr(int now,int l,int r,int p,int c){if(l==r){minr[now]=c;return ;}int mid=(l+r)/2;pushdown(now);if(p<=mid)putr(lc,l,mid,p,c);else putr(rc,mid+1,r,p,c);minr[now]=min(minr[lc],minr[rc]);}void fix(int now,int l,int r){if(minl[now]>0&&minr[now]>=0)return ;if(l==r){if(minl[now]<=0&&list[tp]!=l)list[++tp]=l;//这个点的左边贡献需要被更改了if(minr[now]<0)list[++tp]=l+1;//右边的点左边贡献需要被更改了return ; }int mid=(l+r)/2;pushdown(now);fix(lc,l,mid);fix(rc,mid+1,r);}
}seg;void do1(int x){if(x>=1&&x<=n)seg.newcal(1,1,n,x);}
void do2(int x){if(x>=1&&x<=n)seg.work(1,1,n,x);}
void do3(int x){if(x>=1&&x<=n)seg.putl(1,1,n,x,cl(x));}
void do4(int x){if(x>=1&&x<=n)seg.putr(1,1,n,x,cr(x));}void go(int x)
{if(x<1||x>n)return ;do1(x);do1(x-1);do1(x+1);ok[x]=f1(x);L[x]=cl(x);R[x]=cr(x);do2(x);do3(x+1);do4(x-1);
}int main()
{n=read();m=read();a[0]=999999999;for(int i=1;i<=n;i++)a[i]=read(),ok[i]=f1(i);ok[n+1]=1;for(int i=1;i<=n;i++)L[i]=cl(i),R[i]=cr(i);seg.buildtree(1,1,n);while(m--){int opt=read(),u=read(),v=read();if(opt==2)pr2(seg.qry(1,1,n,u,v));else{seg.modify(1,1,n,u,v);tp=0;seg.fix(1,1,n);for(int i=1;i<=tp;i++)go(list[i]);go(u);go(v+1);}}return 0;
}
[bzoj5050][线段树]建造摩天楼相关推荐
- POJ Mayor's posters——线段树+离散化
原文:http://blog.163.com/cuiqiongjie@126/blog/static/85642734201261151553308/ 大致题意: 有一面墙,被等分为1QW份,一份的宽 ...
- BZOJ2957:楼房重建(线段树)
Description 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子. 为了简化问题,我们考虑这些 ...
- 【BZOJ3073】[Pa2011]Journeys 线段树+堆优化Dijkstra
[BZOJ3073][Pa2011]Journeys Description Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路.N个国家很快建造好了,用1..N编号,但是他发现道路实在 ...
- l2-004 这是二叉搜索树吗? (25分)_什么是 “线段树” ?
线段树是一个复杂的数据结构,比较难理解,也比较难解释清楚.在我将这个数据结构反复学习了五遍的时候,我终于有了信心写出这篇介绍线段树的文章.希望大家能够掌握这种数据结构. 这篇文章比较长,建议大家耐心阅 ...
- 【BZOJ3387】[Usaco2004 Dec]Fence Obstacle Course栅栏行动 线段树
[BZOJ3387][Usaco2004 Dec]Fence Obstacle Course栅栏行动 Description 约翰建造了N(1≤N≤50000)个栅栏来与牛同乐.第i个栅栏的z坐标为[ ...
- [BZOJ 2957]楼房重建(线段树)
Description 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子. 为了简化问题,我们考虑这些 ...
- 主席树——多棵线段树的集合
主席树: (不要管名字) 我们有的时候,会遇到很多种情况,对于每一种情况,都需要通过线段树的操作实现. 碰巧的是,相邻两种情况下的线段树的差异不大.(总体的差异次数是O(N)级别的,均摊就是O(常数) ...
- bzoj2957 奥妙重重的线段树
https://www.lydsy.com/JudgeOnline/problem.php?id=2957 线段树的query和update竟然还可以结合起来用! 题意:小A的楼房外有一大片施工工地, ...
- [帝皇杯day 1] [NOIP2018模拟赛]小P的loI(暴力+素筛),【NOIP模拟赛】创世纪(贪心),无聊的数对(线段树)
文章目录 T1:小P的lol title solution code T2:创世纪 title solution code T3:无聊的数对 title solution code T1:小P的lol ...
最新文章
- Python打印json文件,实现输出
- 吐槽一下现在的代码编辑器
- 96. (GO)不同的二叉搜索树
- python typing typescript_将 python 数据转化为 TypeScript 格式
- python神器pandas_Python数据处理神器,pandas入门必需理解的核心内容
- 计算机桌面老是弹出广告,桌面老是弹出广告怎么办? 爱问知识人
- vulfocus靶场安装教程
- 用java设计秒表_运用Java编写 秒表程序
- zbb20180913 java synchronized同步静态方法和同步非静态方法的异同
- 通过实验来理解代理ARP(二)
- movelast对数据记录数有要求吗_客户验厂,电脑坏了,考勤记录数据可以自动生成找回来吗?...
- 主进程退出后子进程还会存在吗?_[docker]从一个实例,一窥docker进程管理
- 基于springboot的社区核酸检测统计管理系统
- 【Android 教程系列第 10 篇】史上最全的 Android 应用包名汇总,含主流应用商店包名,持续更新...
- 普通人也可以制作App
- 腐蚀rust服务器命令_腐蚀rust服务器命令一览 腐蚀rust有哪些命令
- c语言与或非,单片机avr c语言位运算 与或非 异或逻辑 运算介绍 详解
- Golang 定时器timer和ticker
- scala 编程(其五)集合,交差交差集
- 排列组合之生成排列_(:з」∠)_