扶桑号战列舰 (单调栈+线段树区间更新懒惰标记 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都是游戏迷,“模拟都市”是他们非常喜欢的一个游戏,在这个游戏里面他们 ...
随机推荐
- adb安装apk包时提示:device unauthorized
adb install apk时提示device unauthorized,手机上还没出现usb是否允许调试的询问弹框: 解决方法: 1.cmd输入:adb kill-server,点击回车键 2.c ...
- 第三方数据库管理工具Navicat使用教程
一.Navicat Premium是一个功能强大的第三方数据库管理工具,可以连接管理MySQL.Oracle.PostgreSQL.SQLite 及 SQL Server数据库. 使用Navicat软 ...
- 在liferay 7中如何删除service builder已经生成的数据库table
在Liferay 7中,加了数据库保护机制,你改了service.xml的结构后,重新运行service builder,并不会帮你生成新的数据库表.然后你发现你在数据库中自己手动删除了表后,重新部署 ...
- SQLServer → 09:索引
一.索引概念 用途 我们对数据查询及处理速度已成为衡量应用系统成败的标准,而采用索引来加快数据处理速度通常是最普遍采用的优化方法. 概念 索引是一个单独的,存储在磁盘上的数据结构,它们包含则对数据表里 ...
- Nginx教程(三) Nginx日志管理 (转)
Nginx教程(三) Nginx日志管理 1 日志管理 1.1 Nginx日志描述 通过访问日志,你可以得到用户地域来源.跳转来源.使用终端.某个URL访问量等相关信息:通过错误日志,你可以得到系统某 ...
- input输入框限制录入类型
输入大小写字母.数字.下划线:<input type="text" onkeyup="this.value=this.value.replace(/[^\w_]/g ...
- 2018-2-13-图论-Warshall-和Floyd-矩阵传递闭包
title author date CreateTime categories 图论 Warshall 和Floyd 矩阵传递闭包 lindexi 2018-2-13 17:23:3 +0800 20 ...
- pl/sql基础知识—函数快速入门
n 函数 函数用于返回特定的数据,当建立函数式,在函数头部必须包含return子句,而在函数体内必须包含return语句返回的数据,我们可以使用create function来建立函数,实际案例: ...
- PersistGate轻松几步让Redux实现数据持久化
在开发的过程中,数据用redux管理,觉得希望将数据持久化保存,也就是说当用户下一次打开app或网站的时候,我们希望浏览器/APP自动加载出上次的数据,怎么办?有没有一个
- oracle交互命令
(1)说明:可以替代变量,而该变量在执行时,需要用户输入. sql>select * from emp where job=’&job’; (2)edit 说明:该命令可以编辑指定的s ...