扶桑号战列舰 (单调栈+线段树区间更新懒惰标记 or 栈)
•题目描述
题目描述
众所周知,一战过后,在世界列强建造超无畏级战列舰的竞争之中,旧日本海军根据“个舰优越主义”,建造了扶桑级战列舰,完工时为当时世界上武装最为强大的舰只。
同时,扶桑号战列舰也是舰岛最为科幻的战列舰。
当然,要建造这样的舰船,科技水平是必须的。
同样众所周知的是,德意志科学技术天下第一,所以IJN的司令官从德国学来了一种先进的建船方法。
一只战舰横过来可以看做一个长度为n的序列,每个位置有一个数ai表示这个位置设计的高度。这种先进的造船技术可以每次将一个区间[l,r]内的所有位置高度都+,求到达最终设计状态的最少操作次数。
如果你不能及时完成的话,IJN司令官会奖励你去参加苏里高海战。 输入
第一行包含一个整数n,表示序列的长度。
第二行包含n个非负整数a1,a2,a3,…,an,表示最终的状态。 输出
输出的第一行是一个正整数m,表示最少的操作次数。
接下来m行每行两个正整数li,ri,表示一次操作。
你需要保证1≤li≤ri≤n。
保证最少次数m≤,输出可以以任意顺序输出。 样例输入 样例输出 数据范围:n <= ;
•题解
由初始状态 n 个 0 ,每次操作 +1,变为 a1,a2,...,an ;
可转化为由最终状态 a1,a2,...,an,每次操作 -1,变为 n 个 0;
由操作次数 m ≤ 105 可知 ai ≤ 105;
首先用单调栈求出以 ai 为最小值的左右区间 [li,ri];
然后,从小到大开始枚举 ai,将 [li,ri] 区间的所有数都减 ai;
区间减数的过程用线段树维护;
总的时间复杂度 $O(nlogn)$;
•Code
#include<bits/stdc++.h>
using namespace std;
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
const int maxn=1e5+; int n;
int a[maxn];
vector<int >p[maxn];///ai ≤ 1e5,所以可以开个1e5大小的p存ai出现的位置
int l[maxn];
int r[maxn];
stack<int >sta;
struct Seg
{
int l,r;
int lazy;
int val;
int mid(){return l+((r-l)>>);}
}seg[maxn<<];
struct Data
{
int l,r;
int cnt;
}ans[maxn]; void buildSegTree(int l,int r,int pos)
{
seg[pos].l=l;
seg[pos].r=r;
seg[pos].lazy=; if(l == r)
{
seg[pos].val=a[l];
return ;
}
int mid=l+((r-l)>>);
buildSegTree(l,mid,ls(pos));
buildSegTree(mid+,r,rs(pos));
}
void pushDown(int pos)
{
int &lazy=seg[pos].lazy; seg[ls(pos)].lazy += lazy;
seg[rs(pos)].lazy += lazy; lazy=;
}
int Query(int x,int pos)
{
if(seg[pos].l == seg[pos].r)
return seg[pos].val-seg[pos].lazy; pushDown(pos); int mid=seg[pos].mid(); if(x <= mid)
return Query(x,ls(pos));
else
return Query(x,rs(pos));
}
void Updata(int l,int r,int lazy,int pos)
{
if(seg[pos].l == l && seg[pos].r == r)
{
seg[pos].lazy += lazy;
return ;
}
pushDown(pos); int mid=seg[pos].mid(); if(r <= mid)
Updata(l,r,lazy,ls(pos));
else if(l > mid)
Updata(l,r,lazy,rs(pos));
else
{
Updata(l,mid,lazy,ls(pos));
Updata(mid+,r,lazy,rs(pos));
}
}
void Clear()
{
while(!sta.empty())
sta.pop();
}
void Work()
{
Clear();
for(int i=;i <= n;++i)
{
while(!sta.empty() && a[sta.top()] >= a[i])
sta.pop(); l[i]=sta.empty() ? :sta.top()+;
sta.push(i);
}
Clear();
for(int i=n;i >= ;--i)
{
while(!sta.empty() && a[sta.top()] >= a[i])
sta.pop(); r[i]=sta.empty() ? n:sta.top()-;
sta.push(i);
}
}
void Solve()
{
Work();
buildSegTree(,n,); int k=;
int x=*max_element(a+,a+n+);
for(int i=;i <= x;++i)///从小到大枚举ai
{
for(int j=;j < p[i].size();++j)
{
int id=p[i][j];
int cur=Query(id,);///线段树查询当前的ai在之前的减法中还剩多少 if(cur)///cur > 0
{
Updata(l[id],r[id],cur,);///l[id],r[id] 区间做cur次-1操作
ans[++k]=Data{l[id],r[id],cur};///记录
}
}
}
int m=;
for(int i=;i <= k;++i)
m += ans[i].cnt; printf("%d\n",m);
for(int i=;i <= k;++i)
for(int j=;j <= ans[i].cnt;++j)
printf("%d %d\n",ans[i].l,ans[i].r);
}
int main()
{
scanf("%d",&n);
for(int i=;i <= n;++i)
{
scanf("%d",a+i);
p[a[i]].push_back(i);
} Solve(); return ;
}
•简洁做法
参考资料:中国石油大学(华东), 韩宝坤
•我的理解
巧妙的利用栈,用很简洁的代码实现了上述很繁琐的程序,tql;
首先,如何求解最少的操作次数呢?
找上升部分,如下图( x轴代表点对,y轴代表相应点对到达的高度,n = 9);
在图上虚构两个点,即 0点 和 n+1 点,并使他们的高度为 0;
你会发现对紫色部分的操作是必不可少的;
例如图中的点 2,3 ,在点 2 下降到 0 位置后,点 3 必定还要再下降一次才可以到达 0 位置;
所以所,上升的高度之和便是最少的操作次数,即上图紫色部分的加和;
那么如何求解 m 次操作区间呢?
找下降部分;
对于点 i,如果 a[ i ] > a[ i+1 ] ,那么,通过前面必须要下降的紫色部分让 i 点下降 a[ i ]-a[ i+1 ] 次;
使得 i 点与 i+1 点水平;
一直执行上述操作,直到来到 n 点,通过前面的紫色部分让 n 点下降到 0 点;
至此,执行完毕,共操作 m 次,使得全部点对都置于 0 点;
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+; int n;
int a[maxn];
stack<int >sta; void Solve()
{
int m=;
for(int i=;i <= n;++i)
{
if(a[i] > a[i-])
m += a[i]-a[i-];
} printf("%d\n",m); for(int i=;i <= n;++i)
{
if(a[i] > a[i-])
{
for(int j=a[i-];j < a[i];++j)
sta.push(i);
}
if(a[i] > a[i+])
{
///不用特判sta是否为空,因为下降前必定会有上升,上升的高度必然>=下降的高度
for(int j=a[i];j > a[i+];--j)
{
printf("%d %d\n",sta.top(),i);
sta.pop();
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i=;i <= n;++i)
scanf("%d",a+i); Solve(); return ;
}
扶桑号战列舰 (单调栈+线段树区间更新懒惰标记 or 栈)的更多相关文章
- 【HDU 4614】Vases and Flowers(线段树区间更新懒惰标记)
题目0到n-1的花瓶,操作1在下标a开始插b朵花,输出始末下标.操作2清空[a,b]的花瓶,求清除的花的数量.线段树懒惰标记来更新区间.操作1,先查询0到a-1有num个空瓶子,然后用线段树的性质,或 ...
- hdu1698 Just a Hook (线段树区间更新 懒惰标记)
Just a Hook Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- FZU-1608 Huge Mission 线段树(更新懒惰标记)
题目链接: https://cn.vjudge.net/problem/FZU-1608 题目大意: 长度n,m次操作:每次操作都有三个数:a,b,c:意味着(a,b]区间单位长度的价值为c,若某段长 ...
- 杭电 HDU ACM 1698 Just a Hook(线段树 区间更新 延迟标记)
欢迎"热爱编程"的高考少年--报考杭州电子科技大学计算机学院 Just a Hook Time Limit: 4000/2000 MS (Java/Others) Memor ...
- ZOJ 1610 Count the Color(线段树区间更新)
描述Painting some colored segments on a line, some previously painted segments may be covered by some ...
- HDU 1698 Just a Hook(线段树区间更新查询)
描述 In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of the heroes ...
- hdu 3966(树链剖分+线段树区间更新)
传送门:Problem 3966 https://www.cnblogs.com/violet-acmer/p/9711441.html 学习资料: [1]线段树区间更新:https://blog.c ...
- HDU 1556 Color the ball(线段树区间更新)
Color the ball 我真的该认真的复习一下以前没懂的知识了,今天看了一下线段树,以前只会用模板,现在看懂了之后,发现还有这么多巧妙的地方,好厉害啊 所以就应该尽量搞懂 弄明白每个知识点 [题 ...
- hihoCoder 1080 : 更为复杂的买卖房屋姿势 线段树区间更新
#1080 : 更为复杂的买卖房屋姿势 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho都是游戏迷,“模拟都市”是他们非常喜欢的一个游戏,在这个游戏里面他们 ...
随机推荐
- BZOJ 1925地精部落题解
题目链接 一道神仙题,有很多思考的方式,这里选择最好理解的一种来讲 我们将序列分为两种,一种开头递增,一种开头递减,显然这两种序列的数目是一样的 现在我们只用考虑开头递增的情况 f[i][j]表示前i ...
- windows10 中微信(UWP)版本不显示通知消息
前言: 前段时间笔者更换了升级了WINDOWS10系统,从应用商店安装微信后,使用期间不会推送消息通知,右下角的通知栏也无法添加微信图标.搜索百度和Google后,发现很多人都是这样,这是微信(UWP ...
- 两种方法使vue实现jQuery调用
引言 如果说vue是前端工程化使用较多的骨架,那么JavaScript就是我们的前端的细胞.MVVM模式让我们体验到前端开发的便携,无需再过多的考虑DOM的操作.而vue的渐进式开发(逐步引用组件,按 ...
- 什么是PV UV
PV是网站分析的一个术语,用以衡量网站用户访问的网页的数量.对于广告主,PV值可预期它可以带来多少广告收入.一般来说,PV与来访者的数量成正比,但是PV并不直接决定页面的真实来访者数量,如同一个来访者 ...
- 高速求幂 POW优化
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u013497151/article/details/27633731 #include <io ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 全书总结
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 全书总结 本系列文章中可能有很多翻译有问题或者错误的地方:并且有些章节 ...
- Leetcode811.Subdomain Visit Count子域名访问计数
一个网站域名,如"discuss.leetcode.com",包含了多个子域名.作为顶级域名,常用的有"com",下一级则有"leetcode.com ...
- 使用VirtualBox + Vagrant打造统一的开发环境
https://blog.csdn.net/openn/article/details/54927375 配置步骤 安装VirtualBox 虚拟系统运行在VirtualBox中,类似的工具还有VMw ...
- 利用IDEA构建springboot应用
前提注意: 1.版本,java 1.8 maven 3.3.9 配置项目 项目版本 项目保存路径 在maven里面的conf里面的settings.xml里配置maven中央仓库 (阿里云) ...
- 2019-10-18-dotnet-文件读写务必注意事项
title author date CreateTime categories dotnet 文件读写务必注意事项 lindexi 2019-10-18 08:42:53 +0800 2019-10- ...
