扶桑号战列舰 (单调栈+线段树区间更新懒惰标记 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都是游戏迷,“模拟都市”是他们非常喜欢的一个游戏,在这个游戏里面他们 ...
随机推荐
- java 4对象群体的组织
两个接口 collecion接口 元素构成的元素的群体 map接口 键值对组成的群体 Array类 Vector ArrayList 在数组上构建的类 Java集合框架介绍 集成过得数据结构 查询方法 ...
- ubuntu上设置截图快捷键
ubuntu自带的截图工具感觉能够满足基本的截图功能,可以不必安装另外的截图软件. 一般用到的截图类型有三种:全屏.当前活动窗口.自定义区域,其中自定义区域截图是最灵活也是我们用的最多的方式.在ubu ...
- [C#] 使用WebSocket进行通讯
客户端 客户端很简单 string url = "ws://localhost:24900/" + "test.ashx"; try { System.Net. ...
- mac下的抓包工具Charles
在mac下面,居然没有好的抓包工具,这让我十分纠结,毕竟不可能为了抓一个http包就跑到win下折腾.或许有人说tcpdump这么好的工具,你怎么不用.说实话,tcpdump太复杂了,我还没有细看,再 ...
- ListView组件中 onEndReached 方法在滚动到距离列表最底部一半时执行
初次使用ListView,在写列表滚动到最底部自动加载使用到方法onEndReached, 发现: ListView组件中 onEndReached 方法在滚动到距离列表最底部一半时执行, 于是翻看文 ...
- python三种导入模块的方法和区别
方法一: import modname 模块是指一个可以交互使用,或者从另一Python 程序访问的代码段.只要导入了一个模块,就可以引用它的任何公共的函数.类或属性.模块可以通过这种方法来 使用其它 ...
- Eclipse中提示 找不到类 javax.servlet.http.HttpServletResponse
问题如题, 解决方案如下: 复制tomcat的安装路径下\lib\servlet-api.jar 到WEB-INF/lib下即可.
- hdu5137 枚举删点
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; ; ; ...
- oracle交互命令
(1)说明:可以替代变量,而该变量在执行时,需要用户输入. sql>select * from emp where job=’&job’; (2)edit 说明:该命令可以编辑指定的s ...
- jQuery 五角星评分
五角星打分 我用的是搜狗输入法上带的特殊符号打出来的 空五角星:☆ 实五角星:★ 1.html <ul class="comment"> <li>☆&l ...