扶桑号战列舰 (单调栈+线段树区间更新懒惰标记 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都是游戏迷,“模拟都市”是他们非常喜欢的一个游戏,在这个游戏里面他们 ...
随机推荐
- <第一周> city中国城市聚类 testdata学生上网聚类 例子
中国城市聚类 # -*- coding: utf-8 -*- kmeans算法 """ Created on Thu May 18 22:55:45 2017 @auth ...
- GeoServer手动发布本地Shapefile地图
首先,本文实现的结果图给大家展现一下: 放大的样子: 颜色是通过属性中某个字段值来分级的,可以自定义. 上面功能是用ArcGIS切片好数据,在Geoserver 中发布,并用google地图作为底图展 ...
- @codeforces - 717A@ Festival Organization
目录 @description@ @solution@ @accepted code@ @details@ @description@ 一个长度为 n 的 01 序列是好的,当且仅当该序列任意两个 0 ...
- 网络流24题 最小路径覆盖(DCOJ8002)
题目描述 给定有向图 G=(V,E) G = (V, E)G=(V,E).设 P PP 是 G GG 的一个简单路(顶点不相交)的集合.如果 V VV 中每个顶点恰好在 P PP 的一条路上,则称 P ...
- JavaScript--模拟百度搜索下拉li
上效果: 主要思路: 函数indexOf() .join().innerHTML的使用,还有 用完的数组要清空 <!DOCTYPE html> <html> <head ...
- python系列之(3)爬取豆瓣图书数据
上次介绍了beautifulsoup的使用,那就来进行运用下吧.本篇将主要介绍通过爬取豆瓣图书的信息,存储到sqlite数据库进行分析. 1.sqlite SQLite是一个进程内的库,实现了自给自足 ...
- jupyter的简单操作
jupyter简单使用 esc+ m 切换到标记模式 shift + enter 运行 a 向上新增代码块 b 向下新增代码块 dd 删除代码块 y python代码模式 file --- downl ...
- 2018-9-19-Roslyn-通过-Nuget-管理公司配置
title author date CreateTime categories Roslyn 通过 Nuget 管理公司配置 lindexi 2018-9-19 10:57:5 +0800 2018- ...
- SDUT-2119_数据结构实验之链表四:有序链表的归并
数据结构实验之链表四:有序链表的归并 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 分别输入两个有序的整数序列(分别包 ...
- ROS 用 roboware实现节点信息发送和接收
在ros下实现节点编程,实现一个节点发送消息,另一个节点接收.实现方式有多种,可以直接在命令窗口创建工作空间包以及节点,用catkin_make进行编译,添加.bash路径,然后执行rosrun p ...
