扶桑号战列舰 (单调栈+线段树区间更新懒惰标记 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都是游戏迷,“模拟都市”是他们非常喜欢的一个游戏,在这个游戏里面他们 ...
随机推荐
- HTML-DOM实例——实现带样式的表单验证
HTML样式 基于table标签来实现页面结构 <form id="form1"> <h2>增加管理员</h2> <table&g ...
- Apache Camel 与 Spring Boot 集成,通过FTP定时采集、处理文件 (转)
1.概要: 本项目主要是通过在Spring平台上配置Camel.FTP,实现定时从FTP服务器下载文件到本地.解析文件.存入数据库等功能. 2.搭建空项目: Spring Boot有几种自动生成空项目 ...
- Mybatis自查询递归查找子菜单
之前写过 java从数据库读取菜单,递归生成菜单树 今天才发现mybatis也可以递归查询子菜单 先看一下数据库 主键id,名称name,父id,和url 设计菜单类 public class Men ...
- JS放在body与head中的不同
放在body和head其实差不多的,只不过是文档解析的时间不同.浏览器解析html是从上到下的.如果把javascript放在head里的话,则先被解析,但这时候body还没有解析,所以$(#btn) ...
- Zabbix清理历史数据库,缩减表大小
zabbix 由于历史数据过大, 因此导致磁盘空间暴涨, 下面是解决方法步骤: 一.分析数据库: 1. 统计数据库中每个表所占的空间: mysql> SELECT table_name AS ...
- IDEA切换git分支
查看当前所在分支 场景:在多人开发中,需要在主分支的基础上创建一些分支分配给小团队或个人去开发,然后小分支上的小功能开发完毕之后,再merge(合并)到主分支. 1.查看当前所在的分支 下图1.1中是 ...
- 【转载】Combination Sum
Combination Sum Given a set of candidate numbers (C) and a target number (T), find all unique combin ...
- 25-1 request模块介绍
requests模块 - 基于如下5点展开requests模块的学习 什么是requests模块 requests模块是python中原生的基于网络请求的模块,其主要作用是用来模拟浏览器发起请求.功能 ...
- day10-12_线程queue(了解)
queue队列 :使用import queue,用法与进程Queue一样 queue is especially useful in threaded programming when informa ...
- @codeforces - 1217F@ Forced Online Queries Problem
目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个 n 个点的无向图,标号从 1 到 n.一开始没有任何边 ...