扶桑号战列舰 (单调栈+线段树区间更新懒惰标记 or 栈)
传送门
•题目描述
题目描述 众所周知,一战过后,在世界列强建造超无畏级战列舰的竞争之中,旧日本海军根据“个舰优越主义”,建造了扶桑级战列舰,完工时为当时世界上武装最为强大的舰只。 同时,扶桑号战列舰也是舰岛最为科幻的战列舰。 当然,要建造这样的舰船,科技水平是必须的。 同样众所周知的是,德意志科学技术天下第一,所以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 栈)相关推荐
- hdu 3966(树链剖分+线段树区间更新)
传送门:Problem 3966 https://www.cnblogs.com/violet-acmer/p/9711441.html 学习资料: [1]线段树区间更新:https://blog.c ...
- hdu 5692 Snacks(dfs序+线段树区间更新)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5692 解题思路:这道题是树节点的点权更新,而且涉及到子树,常用的思路是利用dfs序,用线段树来对区间进 ...
- hdu 1698(线段树区间更新)
解题思路:线段树区间更新水题. #include<iostream> #include<cstdio> #include<cstring> using namesp ...
- ZOJ 1610 Count the Colors (线段树区间更新)
题目链接 题意 : 一根木棍,长8000,然后分别在不同的区间涂上不同的颜色,问你最后能够看到多少颜色,然后每个颜色有多少段,颜色大小从头到尾输出. 思路 :线段树区间更新一下,然后标记一下,最后从头 ...
- POJ 2777 ZOJ 1610 HDU 1698 --线段树--区间更新
直接将这3题 放一起了 今天在做线段树的东西 这3个都是区间更新的 查询方式互相不同 反正都可以放到一起吧 直接先上链接了 touch me touch me touch me 关于涉及到区间的修改 ...
- Just a Hook(线段树区间更新)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1698 In the game of DotA, Pudge's meat hook is actual ...
- hihoCoder 1080 : 更为复杂的买卖房屋姿势 线段树区间更新
#1080 : 更为复杂的买卖房屋姿势 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho都是游戏迷,"模拟都市"是他们非常喜欢的一个游戏 ...
- CodeForces - 272C Dima and Staircase (线段树区间更新)
题意: 见以下样例,给出 5 个区间,每个区间的高度已知.一共 4 次操作.每次操作都是从最左边开始向下垒一个宽为 w 高为h 的木块,过程见下图. 问每次垒木块的高度是多少? Input 5 1 2 ...
- CDOJ-1057 秋实大哥与花(线段树区间更新)
秋实大哥与花 Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) Submit St ...
最新文章
- 2018-3-8(论文--网络评论中的非结构化信息表示与研究)笔记-----基于证据理论的网络评论综合方法
- python中tolist_python - 无法使用Gremlinpython使用“ .toList()”列出Janusgraph中存在的所有顶点 - 堆栈内存溢出...
- python list合并拼接
- deeplearning4j – 分布式DL开源项目
- Android中TextView中string的特殊符号显示的方法
- 分词之后的如何做特征选择_特征选择怎么做?这篇文章告诉你
- vs点击方法跳不到对于的地方_内脏脂肪怎么测?这个方法也太方便了!| EASD 2020...
- 识别视频文件夹,listview
- 天网防火墙去掉更新提示对话框
- 开源机器人项目Hands Free
- 什么是PID控制中的超调
- Linux的wget命令详解
- MonoRail学习笔记四:MonoRail基本流程分析
- 曲靖师范计算机科学与技术,曲靖师范学院计算机科学与工程学院试讲表.doc
- bzoj 2299(裴蜀定理)
- 计算机会议在美国的英语文章,英文一篇计算机国际会议的开幕词
- Python热门吗 前景怎么样
- mongodb高级聚合查询
- Android工程师应该具备的四大开发习惯
- 库存软件测试培训,软件测试商品库存的管理.doc
热门文章
- mpvue vue 长按录音,上滑取消,下拉恢复
- linux bridge 添加fdb,bridge fdb 与vxlan
- linux服务端查看firebox版本,[图]Linux端Firefox 84将默认启用WebRender
- React Native - 使用Vibration API实现设备振动
- RSA算法加密解密举例
- 欧姆龙e5dc温控器_E5DC-QX2DSM-800手册欧姆龙 数字温度控制器E5DC-QX2DSM-800用户手册 - 广州凌控...
- SpringBoot 整合zxing生成或解析二维码
- 程序人生 - 提前冲线!10名浙江高中生不用高考,直接被清华大学录取,杭州4位学霸来自这些学校
- 2022年信息学部物联网工程学院学生科协第二次软件大培训
- 论文阅读(9)---基于Transformer的多模态CNN心电图心律失常分类