「日常训练&知识学习」单调栈
这几天的知识学习比较多,因为时间不够了。加油吧,为了梦想。
这里写几条简单的单调栈作为题解记录,因为单调栈的用法很简单,可是想到并转化成用这个需要一些题目的积淀。
相关博客参见:https://blog.csdn.net/wubaizhe/article/details/70136174
POJ 3250 Bad Hair Day
题意与分析
题意是这样的:\(n\)个牛排成一列向右看,牛\(i\)能看到牛\(j\)的头顶,当且仅当牛\(j\)在牛\(i\)的右边并且牛\(i\)与牛\(j\)之间的所有牛均比牛\(i\)矮。设牛\(i\)能看到的牛数为\(C_i\),求\(\Sigma C_i\)。
分析:注意到没有?每个牛只能向右看,看到的都是比自己矮的等价于每头牛能被自己左边且比自己大的牛看见,这是啥?单调啊!维护一个单调栈,确定每头牛左边有几头牛比自己高的,然后求和即可。
代码
/*
* Filename: poj3250.cpp
* Date: 2018-11-13
*/
#include <iostream>
#include <cstring>
#include <stack>
#define INF 0x3f3f3f3f
#define PB push_back
#define MP make_pair
#define fi first
#define se second
#define rep(i,a,b) for(repType i=(a); i<=(b); ++i)
#define per(i,a,b) for(repType i=(a); i>=(b); --i)
#define ZERO(x) memset(x, 0, sizeof(x))
#define MS(x,y) memset(x, y, sizeof(x))
#define ALL(x) (x).begin(), (x).end()
#define QUICKIO \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)
using namespace std;
typedef long long ll;
typedef int repType;
int
main()
{
QUICKIO
int n;
while(cin>>n)
{
ll sum=0;
stack<ll> s;
ll h,t;
cin>>h;
s.push(h);
rep(i,1,n-1)
{
cin>>t;
while(!s.empty() && t>=s.top()) s.pop();
sum+=s.size();
s.push(t);
}
cout<<sum<<endl;
}
return 0;
}
POJ 2796 Feel Good
题意与分析
题意是这样的:一个数组,对于某个区间,这个区间的和*这个区间中的最小值=这个区间的计算值。求这个数组中的最大计算值,并任意输出其的一个左右位置。
这题很容易想到我之前写的一题:Codeforces Round #305 Div. 2 D——我们需要维护某个数分别是哪些区间的最小值。又注意到这个数组是非负的,那么肯定是越大越好。预先维护一个前缀和,然后\(O(n)\)扫一遍完事了。
这两题给我们一个启示:单调栈可以维护区间最小值(目前看来是离线的情况下),通过维护左边最右的比它小的值的和右边最左的比它小的值。
代码
/*
* Filename: poj2796.cpp
* Date: 2018-11-14
*/
#include <iostream>
#include <cstring>
#include <stack>
#include <cstdio>
#define INF 0x3f3f3f3f
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
#define rep(i,a,b) for(repType i=(a); i<=(b); ++i)
#define per(i,a,b) for(repType i=(a); i>=(b); --i)
#define ZERO(x) memset(x, 0, sizeof(x))
#define MS(x,y) memset(x, y, sizeof(x))
#define ALL(x) (x).begin(), (x).end()
#define QUICKIO \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)
using namespace std;
typedef long long ll;
typedef int repType;
const int MAXN=100005;
int n,l[MAXN],r[MAXN];
int arr[MAXN];
ll prefix[MAXN];
int main()
{
while(scanf("%d",&n)==1)
{
prefix[0]=0;
rep(i,1,n)
{
scanf("%d",&arr[i]);
prefix[i]=prefix[i-1]+arr[i];
}
stack<int> s;
rep(i,1,n)
{
while(!s.empty() && arr[s.top()]>=arr[i])
s.pop();
if(s.empty()) l[i]=0;
else l[i]=s.top();
s.push(i);
}
s=stack<int>();
per(i,n,1)
{
while(!s.empty() && arr[s.top()]>=arr[i])
s.pop();
if(s.empty()) r[i]=n+1;
else r[i]=s.top();
s.push(i);
}
/*
rep(i,1,n)
{
cout<<l[i]<<" "<<r[i]<<endl;
}
*/
ll ans=-1;
int lp=1,rp=1;
rep(i,1,n)
{
ll presum=prefix[r[i]-1]-prefix[l[i]];
//cout<<arr[i]*presum<<endl;
if(ans<arr[i]*presum)
{
ans=arr[i]*presum;
lp=l[i]+1;
rp=r[i]-1;
}
}
printf("%lld\n%d %d\n", ans, lp, rp);
}
return 0;
}
HDU 1506 Largest Rectangle in a Histogram
题意与分析
题意其实就是要找到一个尽可能大的矩形来完全覆盖这个矩形下的所有柱子,只能覆盖柱子,不能留空。每一个柱子都要尽可能向左向右延伸,使得获得最大的面积。
为什么要把这几题连起来写呢?发现没有,这几题各种套路,看似不同,但是转化后思路(联动上面那个Codeforce题目)一模一样!都是去维护一个区间最小值。这里怎么维护呢?考虑以自己为min,扩展的最大区域即可,然后扫一遍\(O(n)\)完事了(Codeforces题目的结论,区间长度为\(r_i-l_i-1\)),哈哈。
代码
/*
* Filename: hdu1506.cpp
* Date: 2018-11-14
*/
#include <iostream>
#include <cstring>
#include <stack>
#include <cstdio>
#define INF 0x3f3f3f3f
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
#define rep(i,a,b) for(repType i=(a); i<=(b); ++i)
#define per(i,a,b) for(repType i=(a); i>=(b); --i)
#define ZERO(x) memset(x, 0, sizeof(x))
#define MS(x,y) memset(x, y, sizeof(x))
#define ALL(x) (x).begin(), (x).end()
#define QUICKIO \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)
using namespace std;
typedef long long ll;
typedef int repType;
const int MAXN=100005;
int n,l[MAXN],r[MAXN];
ll arr[MAXN];
int main()
{
while(scanf("%d",&n)==1)
{
if(n==0) break;
rep(i,1,n)
scanf("%lld",&arr[i]);
stack<int> s;
rep(i,1,n)
{
while(!s.empty() && arr[s.top()]>=arr[i])
s.pop();
if(s.empty()) l[i]=0;
else l[i]=s.top();
s.push(i);
}
s=stack<int>();
per(i,n,1)
{
while(!s.empty() && arr[s.top()]>=arr[i])
s.pop();
if(s.empty()) r[i]=n+1;
else r[i]=s.top();
s.push(i);
}
/*
rep(i,1,n)
{
cout<<l[i]<<" "<<r[i]<<endl;
}
*/
ll ans=-1;
rep(i,1,n)
{
ans=max(ans,arr[i]*(r[i]-l[i]-1));
}
printf("%lld\n", ans);
}
return 0;
}
HDU 5033 Building
题意与分析
题意:有一个人在到处是高楼大厦的地方抬头仰望,假设所有高楼都在一条数轴上,给出了高楼的位置和高度,然后给出了人的位置(高度为0.....),问人能看到的阳光的最大角度是多少。
这题看起来和前面不太一样了,角度而不是最小值区间,可是想不到吧,这也可以单调栈2333
怎么写呢?容易想到,决定一个人的视角的是他左右边的建筑物之间的斜率。如果不容易想到的话,可以参见 https://www.cnblogs.com/chen9510/p/7246889.html 的文章,里面有几幅图片比较生动的表现了这个结论。
然后就是好玩的地方了:我们要在读入所有的查询后,将每个查询点视为高度为0的楼,然后再通过比较两栋相邻楼顶连线的斜率大小维护一个单调栈。这样写就和上面的套路一样一样的了,哈哈。你会从代码中发现这一点。
代码
自闭了,自己的代码死活找不到问题- -拿别人的代码出来学习吧
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=1e5+5;
const double PI=acos(-1);
struct Node{
double x;
double y;
int L,R,id;
}a[2*maxn];
int top,s[maxn];
bool cmp(const Node s1,const Node s2)
{
return s1.x<s2.x;
}
bool cmp2(const Node s1,const Node s2)
{
return s1.id<s2.id;
}
double cal2(double x,double y)
{
//cout<<"H:"<<x<<" "<<y<<endl;
return atan(y/x);
}
int main()
{
///cout << "Hello world!" << endl;
int T,Case=1; cin>>T;
while(T--)
{
printf("Case #%d:\n",Case++);
int N; scanf("%d",&N);
for(int i=1;i<=N;i++) scanf("%lf%lf",&a[i].x,&a[i].y),a[i].id=i;
int Q; scanf("%d",&Q);
for(int i=N+1;i<=N+Q;i++) scanf("%lf",&a[i].x),a[i].y=0,a[i].id=i;
sort(a+1,a+N+Q+1,cmp);
///for(int i=1;i<=N+Q;i++) cout<<a[i].id<<" "<<a[i].x<<" "<<a[i].y<<endl;
top=0; s[0]=1;
for(int i=2;i<=N+Q;i++)
{
while(1)
{
if(top<1) break;
double x1=a[s[top]].x-a[s[top-1]].x;
double y1=a[s[top]].y-a[s[top-1]].y;
//double f1=cal(a[s[top-1]].x, a[s[top-1]].y, a[s[top]].x, a[s[top]].y);
double x2=a[i].x-a[s[top]].x;
double y2=a[i].y-a[s[top]].y;
//double f2=cal(a[s[top]].x, a[s[top]].y, a[i].x, a[i].y);
if(y1*x2>y2*x1) break;
top--;
}
if(a[i].id>N) a[i].L=a[s[top]].id;//,cout<<"top1 "<<top<<" "<<s[top]<<" "<<a[s[top]].id<<endl;
s[++top]=i;
}
top=0; s[0]=N+Q;
for(int i=N+Q-1;i>=1;i--)
{
while(1)
{
if(top<1) break;
double x1=a[s[top-1]].x-a[s[top]].x;
double y1=a[s[top-1]].y-a[s[top]].y;
//double f1=cal(a[s[top-1]].x, a[s[top-1]].y, a[s[top]].x, a[s[top]].y);
double x2=a[s[top]].x-a[i].x;
double y2=a[s[top]].y-a[i].y;
//double f2=cal(a[s[top]].x, a[s[top]].y, a[i].x, a[i].y);
if(y1*x2<y2*x1) break;
top--;
}
if(a[i].id>N) a[i].R=a[s[top]].id;//,cout<<"top2 "<<top<<" "<<s[top]<<" "<<a[s[top]].id<<endl;
s[++top]=i;
}
sort(a+1,a+N+Q+1,cmp2);
for(int i=N+1;i<=N+Q;i++)
{
//cout<<"--->"<<a[i].L<<" "<<a[i].R<<endl;
double ans=PI;
int k=a[i].L; ans-=cal2(a[i].x-a[k].x,a[k].y);
k=a[i].R; ans-=cal2(a[k].x-a[i].x,a[k].y);
printf("%.10f\n",ans*180/PI);
}
}
return 0;
}
总结
只要你能转化成离线的区间最值问题,那么单调栈有可能能够维护它(当然,问题需要满足一定的性质——这就是我们需要干的活了:直觉、转化、or玄学)。
「日常训练&知识学习」单调栈的更多相关文章
- 「日常训练&知识学习」树的分块(王室联邦,HYSBZ-1086)
题意与分析 这题的题意就是树分块,更具体的看题目(中文题). 学习这一题是为了树的分块,为树上莫队做铺垫. 参考1:https://blog.csdn.net/LJH_KOQI/article/det ...
- 「日常训练&知识学习」莫队算法(二):树上莫队(Count on a tree II,SPOJ COT2)
题意与分析 题意是这样的,给定一颗节点有权值的树,然后给若干个询问,每次询问让你找出一条链上有多少个不同权值. 写这题之前要参看我的三个blog:Codeforces Round #326 Div. ...
- 「日常训练&知识学习」树的直径(POJ-1849,Two)
题意 一个城市由节点和连接节点的街道组成,街道是双向的. 此刻大雪覆盖了这个城市,市长确定了一些街道要将它们清扫干净,这些街道保证所有的节点可以通过它们连通而且街道数目尽可能小. 现有两台相同的扫雪机 ...
- 「国庆训练&知识学习」图的最大独立集与拓展(Land of Farms,HDU-5556)
题意 一个\(N*M\)的矩阵,其中"."代表空地,"0-9"代表古代建筑,我们如果选择了一个编号的古代建筑想要建立,那么对应就要将全部该编号的建筑建立起来,如 ...
- 「日常训练」Caterpillar(POJ-3310)
题意与分析 一条很有趣的题目.给一个无向图,问它是否无环,且可以在上面找到一条线,使所有的顶点要么在线上要么不在线上但在与线相连的边上. 那么首先要确定所有点联系在一起.这个可以同判环一起处理:如果建 ...
- 「日常训练」ZgukistringZ(Codeforces Round #307 Div. 2 B)
题意与分析(CodeForces 551B) 这他妈哪里是日常训练,这是日常弟中弟. 题意是这样的,给出一个字符串A,再给出两个字符串B,C,求A中任意量字符交换后(不限制次数)能够得到的使B,C作为 ...
- 「日常训练」Mike and Feet(Codeforces Round #305 Div. 2 D)
题意 (Codeforces 548D) 对一个有$n$个数的数列,我们要求其连续$x(1\le x\le n)$(对于每个$x$,这样的连续group有若干个)的最小数的最大值. 分析 这是一道用了 ...
- 「日常训练」 Fire!(UVA-11624)
与其说是训练不如说是重温.重新写了Java版本的代码. import java.util.*; import java.math.*; import java.io.BufferedInputStre ...
- 「日常训练」Battle Over Cities - Hard Version(PAT-TOP-1001)
题意与分析 题意真的很简单,实在不想讲了,简单说下做法吧. 枚举删除每个点,然后求最小生成树,如果这个路已经存在那么边权就是0,否则按照原来的处理,之后求花费,然后判整个图是否联通(并查集有几个roo ...
随机推荐
- luogu P3941 入阵曲
嘟嘟嘟 这道题我觉得跟最大子矩阵那道题非常像,都是O(n4)二维前缀和暴力很好想,O(n3)正解需要点转化. O(n4)暴力就不说啦,二维前缀和,枚举所有矩形,应该能得55分. O(n3)需要用到降维 ...
- 使用@Aspect切面进行让JDBC自动关闭(Spring AOP)
原生jdbc进行测试 demo:https://github.com/weibanggang/springjdbcAspect.git
- SecureCRT连接主机时(串口/网络),无法从键盘输入
Session Option-Connection-Serial-Flow Control,里面的选项全部取消掉,再重启CRT就ok了...
- CSS Variables
CSS原生变量(CSS自定义属性) 示例地址:https://github.com/ccyinghua/Css-Variables 一.css原生变量的基础用法 变量声明使用两根连词线"-- ...
- Oracle在线重定义(online redefinition)--将普通表改为分区表
使用Oracle的在线重定义技术,可以将Oracle的普通表改为分区表.操作如下: STEP1:测试表是否可以在线重定义,这里以unixdev数据库的LIJIAMAN.BSTEST为例 EXEC DB ...
- django, CentOS7+nginx(apache)+mod_wsgi+Python+Django
Django是一个开放源代码的Web应用框架,由Python写成.采用了MVC的框架模式,即模型M,视图V和控制器C.它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是CMS ...
- 【oracle笔记2】约束
约束 *约束是添加在列上的,用来约束列的. 1. 主键约束(唯一标识) ***非空*** ***唯一*** ***被引用***(外键时引用主键) *当表的某一列被指定为主键后,该列就不能为空,不能有重 ...
- BZOJ2286: [Sdoi2011]消耗战(虚树/树形DP)
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 5246 Solved: 1978[Submit][Status][Discuss] Descript ...
- 基于jquery,ajax请求及自我终止的函数封装。
场景描述: 在我们平时的开发过程中,经常会遇到这样的情况.在搜索功能中进行模糊搜索或者联想关联. 这就要我们每次对输入框中的数据进行改动时,都要发送一次请求.当在短时间内多次操作改动时,问题就出现了. ...
- Java OOP——第六章 框架集合
1.集合框架包含的主要内容及彼此之间的关系: 图1: 集合框架:是为了表示和操作集合而统一规定的一种统一的标准体系结构. 包含三大块的内容:对外的接口.接口的是实现和对 ...