传送门

•题目描述

题目描述
众所周知,一战过后,在世界列强建造超无畏级战列舰的竞争之中,旧日本海军根据“个舰优越主义”,建造了扶桑级战列舰,完工时为当时世界上武装最为强大的舰只。
同时,扶桑号战列舰也是舰岛最为科幻的战列舰。
当然,要建造这样的舰船,科技水平是必须的。
同样众所周知的是,德意志科学技术天下第一,所以IJN的司令官从德国学来了一种先进的建船方法。
一只战舰横过来可以看做一个长度为n的序列,每个位置有一个数ai表示这个位置设计的高度。这种先进的造船技术可以每次将一个区间[l,r]内的所有位置高度都+1,求到达最终设计状态的最少操作次数。
如果你不能及时完成的话,IJN司令官会奖励你去参加苏里高海战。输入
第一行包含一个整数n,表示序列的长度。
第二行包含n个非负整数a1,a2,a3,…,an,表示最终的状态。输出
输出的第一行是一个正整数m,表示最少的操作次数。
接下来m行每行两个正整数li,ri,表示一次操作。
你需要保证1≤li≤ri≤n。
保证最少次数m≤105,输出可以以任意顺序输出。样例输入
6
2 3 3 3 3 3样例输出
3
1 6
1 6
2 6数据范围:n <= 100000;

View Code

•题解

  由初始状态 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

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define ls(x) (x<<1)
  4 #define rs(x) (x<<1|1)
  5 const int maxn=1e5+50;
  6
  7 int n;
  8 int a[maxn];
  9 vector<int >p[maxn];///ai ≤ 1e5,所以可以开个1e5大小的p存ai出现的位置
 10 int l[maxn];
 11 int r[maxn];
 12 stack<int >sta;
 13 struct Seg
 14 {
 15     int l,r;
 16     int lazy;
 17     int val;
 18     int mid(){return l+((r-l)>>1);}
 19 }seg[maxn<<4];
 20 struct Data
 21 {
 22     int l,r;
 23     int cnt;
 24 }ans[maxn];
 25
 26 void buildSegTree(int l,int r,int pos)
 27 {
 28     seg[pos].l=l;
 29     seg[pos].r=r;
 30     seg[pos].lazy=0;
 31
 32     if(l == r)
 33     {
 34         seg[pos].val=a[l];
 35         return ;
 36     }
 37     int mid=l+((r-l)>>1);
 38     buildSegTree(l,mid,ls(pos));
 39     buildSegTree(mid+1,r,rs(pos));
 40 }
 41 void pushDown(int pos)
 42 {
 43     int &lazy=seg[pos].lazy;
 44
 45     seg[ls(pos)].lazy += lazy;
 46     seg[rs(pos)].lazy += lazy;
 47
 48     lazy=0;
 49 }
 50 int Query(int x,int pos)
 51 {
 52     if(seg[pos].l == seg[pos].r)
 53         return seg[pos].val-seg[pos].lazy;
 54
 55     pushDown(pos);
 56
 57     int mid=seg[pos].mid();
 58
 59     if(x <= mid)
 60         return Query(x,ls(pos));
 61     else
 62         return Query(x,rs(pos));
 63 }
 64 void Updata(int l,int r,int lazy,int pos)
 65 {
 66     if(seg[pos].l == l && seg[pos].r == r)
 67     {
 68         seg[pos].lazy += lazy;
 69         return ;
 70     }
 71     pushDown(pos);
 72
 73     int mid=seg[pos].mid();
 74
 75     if(r <= mid)
 76         Updata(l,r,lazy,ls(pos));
 77     else if(l > mid)
 78         Updata(l,r,lazy,rs(pos));
 79     else
 80     {
 81         Updata(l,mid,lazy,ls(pos));
 82         Updata(mid+1,r,lazy,rs(pos));
 83     }
 84 }
 85 void Clear()
 86 {
 87     while(!sta.empty())
 88         sta.pop();
 89 }
 90 void Work()
 91 {
 92     Clear();
 93     for(int i=1;i <= n;++i)
 94     {
 95         while(!sta.empty() && a[sta.top()] >= a[i])
 96             sta.pop();
 97
 98         l[i]=sta.empty() ? 1:sta.top()+1;
 99         sta.push(i);
100     }
101     Clear();
102     for(int i=n;i >= 1;--i)
103     {
104         while(!sta.empty() && a[sta.top()] >= a[i])
105             sta.pop();
106
107         r[i]=sta.empty() ? n:sta.top()-1;
108         sta.push(i);
109     }
110 }
111 void Solve()
112 {
113     Work();
114     buildSegTree(1,n,1);
115
116     int k=0;
117     int x=*max_element(a+1,a+n+1);
118     for(int i=1;i <= x;++i)///从小到大枚举ai
119     {
120         for(int j=0;j < p[i].size();++j)
121         {
122             int id=p[i][j];
123             int cur=Query(id,1);///线段树查询当前的ai在之前的减法中还剩多少
124
125             if(cur)///cur > 0
126             {
127                 Updata(l[id],r[id],cur,1);///l[id],r[id] 区间做cur次-1操作
128                 ans[++k]=Data{l[id],r[id],cur};///记录
129             }
130         }
131     }
132     int m=0;
133     for(int i=1;i <= k;++i)
134         m += ans[i].cnt;
135
136     printf("%d\n",m);
137     for(int i=1;i <= k;++i)
138         for(int j=1;j <= ans[i].cnt;++j)
139             printf("%d %d\n",ans[i].l,ans[i].r);
140 }
141 int main()
142 {
143     scanf("%d",&n);
144     for(int i=1;i <= n;++i)
145     {
146         scanf("%d",a+i);
147         p[a[i]].push_back(i);
148     }
149
150     Solve();
151
152     return 0;
153 }

View Code

•简洁做法

  参考资料:中国石油大学(华东), 韩宝坤

•我的理解

  巧妙的利用栈,用很简洁的代码实现了上述很繁琐的程序,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

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e5+50;
 4
 5 int n;
 6 int a[maxn];
 7 stack<int >sta;
 8
 9 void Solve()
10 {
11     int m=0;
12     for(int i=1;i <= n;++i)
13     {
14         if(a[i] > a[i-1])
15             m += a[i]-a[i-1];
16     }
17
18     printf("%d\n",m);
19
20     for(int i=1;i <= n;++i)
21     {
22         if(a[i] > a[i-1])
23         {
24             for(int j=a[i-1];j < a[i];++j)
25                 sta.push(i);
26         }
27         if(a[i] > a[i+1])
28         {
29             ///不用特判sta是否为空,因为下降前必定会有上升,上升的高度必然>=下降的高度
30             for(int j=a[i];j > a[i+1];--j)
31             {
32                 printf("%d %d\n",sta.top(),i);
33                 sta.pop();
34             }
35         }
36     }
37 }
38 int main()
39 {
40     scanf("%d",&n);
41     for(int i=1;i <= n;++i)
42         scanf("%d",a+i);
43
44     Solve();
45
46     return 0;
47 }

View Code

转载于:https://www.cnblogs.com/violet-acmer/p/11275248.html

扶桑号战列舰 (单调栈+线段树区间更新懒惰标记 or 栈)相关推荐

  1. hdu 3966(树链剖分+线段树区间更新)

    传送门:Problem 3966 https://www.cnblogs.com/violet-acmer/p/9711441.html 学习资料: [1]线段树区间更新:https://blog.c ...

  2. hdu 5692 Snacks(dfs序+线段树区间更新)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5692 解题思路:这道题是树节点的点权更新,而且涉及到子树,常用的思路是利用dfs序,用线段树来对区间进 ...

  3. hdu 1698(线段树区间更新)

    解题思路:线段树区间更新水题. #include<iostream> #include<cstdio> #include<cstring> using namesp ...

  4. ZOJ 1610 Count the Colors (线段树区间更新)

    题目链接 题意 : 一根木棍,长8000,然后分别在不同的区间涂上不同的颜色,问你最后能够看到多少颜色,然后每个颜色有多少段,颜色大小从头到尾输出. 思路 :线段树区间更新一下,然后标记一下,最后从头 ...

  5. POJ 2777 ZOJ 1610 HDU 1698 --线段树--区间更新

    直接将这3题 放一起了  今天在做线段树的东西 这3个都是区间更新的 查询方式互相不同 反正都可以放到一起吧 直接先上链接了 touch me touch me touch me 关于涉及到区间的修改 ...

  6. Just a Hook(线段树区间更新)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1698 In the game of DotA, Pudge's meat hook is actual ...

  7. hihoCoder 1080 : 更为复杂的买卖房屋姿势 线段树区间更新

    #1080 : 更为复杂的买卖房屋姿势 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho都是游戏迷,"模拟都市"是他们非常喜欢的一个游戏 ...

  8. CodeForces - 272C Dima and Staircase (线段树区间更新)

    题意: 见以下样例,给出 5 个区间,每个区间的高度已知.一共 4 次操作.每次操作都是从最左边开始向下垒一个宽为 w 高为h 的木块,过程见下图. 问每次垒木块的高度是多少? Input 5 1 2 ...

  9. CDOJ-1057 秋实大哥与花(线段树区间更新)

    秋实大哥与花 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit St ...

最新文章

  1. 2018-3-8(论文--网络评论中的非结构化信息表示与研究)笔记-----基于证据理论的网络评论综合方法
  2. python中tolist_python - 无法使用Gremlinpython使用“ .toList()”列出Janusgraph中存在的所有顶点 - 堆栈内存溢出...
  3. python list合并拼接
  4. deeplearning4j – 分布式DL开源项目
  5. Android中TextView中string的特殊符号显示的方法
  6. 分词之后的如何做特征选择_特征选择怎么做?这篇文章告诉你
  7. vs点击方法跳不到对于的地方_内脏脂肪怎么测?这个方法也太方便了!| EASD 2020...
  8. 识别视频文件夹,listview
  9. 天网防火墙去掉更新提示对话框
  10. 开源机器人项目Hands Free
  11. 什么是PID控制中的超调
  12. Linux的wget命令详解
  13. MonoRail学习笔记四:MonoRail基本流程分析
  14. 曲靖师范计算机科学与技术,曲靖师范学院计算机科学与工程学院试讲表.doc
  15. bzoj 2299(裴蜀定理)
  16. 计算机会议在美国的英语文章,英文一篇计算机国际会议的开幕词
  17. Python热门吗 前景怎么样
  18. mongodb高级聚合查询
  19. Android工程师应该具备的四大开发习惯
  20. 库存软件测试培训,软件测试商品库存的管理.doc

热门文章

  1. mpvue vue 长按录音,上滑取消,下拉恢复
  2. linux bridge 添加fdb,bridge fdb 与vxlan
  3. linux服务端查看firebox版本,[图]Linux端Firefox 84将默认启用WebRender
  4. React Native - 使用Vibration API实现设备振动
  5. RSA算法加密解密举例
  6. 欧姆龙e5dc温控器_E5DC-QX2DSM-800手册欧姆龙 数字温度控制器E5DC-QX2DSM-800用户手册 - 广州凌控...
  7. SpringBoot 整合zxing生成或解析二维码
  8. 程序人生 - 提前冲线!10名浙江高中生不用高考,直接被清华大学录取,杭州4位学霸来自这些学校
  9. 2022年信息学部物联网工程学院学生科协第二次软件大培训
  10. 论文阅读(9)---基于Transformer的多模态CNN心电图心律失常分类