目录
一.单调栈
二.树状数组
一.单调栈
首先栈的基础操作
入栈:s.push() 出栈:s.pop() 栈顶元素:s.top() 栈中元素个数:s.size()
开栈:stack<node>s
做题步骤:先确定要维护一个怎么样的栈(单调不升/降,递增/减),然后就加亿点点小细节就可以华丽地AK啦~
接下来来看几道例题吧
例题一:
题目描述
给出nn个圆环,每个圆环有内径aiai,外径bibi,高hihi。
圆环xx能放在圆环yy上仅当ay<bx≤byay<bx≤by,问这些圆环最多能搭多高。
输入格式
第一行一个整数n,表示有n个圆环 (1⩽n⩽100000)(1⩽n⩽100000)
接下来n行,每行3个整数,为内径aiai,外径bibi,高hi(1⩽ai,bi,hi⩽109,bi>ai)hi(1⩽ai,bi,hi⩽109,bi>ai)
输出格式
一行,一个整数,表示圆环塔的最大高度。
样例数据
input
3
1 5 1
2 6 2
3 7 3
output
6
input
4
1 2 1
1 3 3
4 6 2
5 7 1
output
4
样例解释:1圆环放在2圆环上,高度为4.
分析:单调栈的入门题,很显然这题我们需要维护一个单调递增栈
#include<bits/stdc++.h>
using namespace std;
long long n,sum,ans;
stack< long long > s;
struct node
{
long long a,b,h;
}k[100086];
bool cmp(node x,node y)
{
if(x.b!=y.b) return x.b>y.b;
return x.a>y.a;
}
int main()
{
freopen("standard.in","r",stdin);
freopen("standard.out","w",stdout);
cin>>n;
for(long long i=1;i<=n;i++) cin>>k[i].a>>k[i].b>>k[i].h;
sort(k+1,k+1+n,cmp);
ans=sum=k[1].h;
s.push(1);
for(long long i=2;i<=n;i++)
{
while(!s.empty() && k[s.top()].a>=k[i].b)
{
sum-=k[s.top()].h;
s.pop();
}
sum+=k[i].h;
s.push(i);
ans=max(ans,sum);
}
cout<<ans<<endl;
return 0;
}
例题二:
题目描述
贤正的某 N 头奶牛 (1 <= N <= 80,000) 正在过乱头发节!由于每头牛都 意识到自己凌乱不堪的发型, 宁智贤希望统计出能够看到其他牛的头发的牛的数量。
每一头牛 i有一个高度 h[i] (1 <= h[i] <= 1,000,000,000)而且面向东方排成 一排(在我们的图中是向右)。因此,第i头牛可以看到她前面的那些牛的头, (即i+1, i+2,等等),只要那些牛的高度严格小于她的高度。
例如这个例子:
牛#1 可以看到她们的发型 #2, 3, 4 牛#2 不能看到任何牛的发型 牛#3 可以看到她的发型 #4 牛#4 不能看到任何牛的发型 牛#5 可以看到她的发型 6 牛#6 不能看到任何牛的发型!
让 c[i] 表示第i头牛可以看到发型的牛的数量;请输出 c[1] 至 c[N]的和。 如上面的这个例子,正确解是3 + 0 + 1 + 0 + 1 + 0 = 5。
输入格式
-
Line 1: 牛的数量 N。
-
Lines 2..N+1: 第 i+1 是一个整数,表示第i头牛的高度。
输出格式
Line 1: 一个整数表示c[1] 至 c[N]的和。
样例数据
input
6
10
3
7
4
12
2
output
5
分析:与上题类似,不过本题需要维护的是单调递减栈
#include<bits/stdc++.h>
using namespace std;
long long n,ans;
long long h[10000086];
stack< long long > s;
int main()
{
freopen("badhair.in","r",stdin);
freopen("badhair.out","w",stdout);
cin>>n;
for(long long i=1;i<=n;i++) cin>>h[i];
for(long long i=1;i<=n;i++)
{
while(!s.empty() && s.top()<=h[i])
{
s.pop();
}
ans+=s.size();
s.push(h[i]);
}
cout<<ans<<endl;
return 0;
}
例题三:
题目描述
现在有N(0<=N<=500 000)个人在一起排队,他们的身高分别为A1,...,AN(0<=Ai<=231},i=1~N)我们规定A能看到B当且仅当AB之间没有比A高的人,那么有多少对人能够相互看到呢?
输入格式
第一行一个非负整数N,第二行N个非负整数A1,...,AN,之间用空格分隔
输出格式
一行一个非负整数,表示相互能看到的人的对数
样例
样例输入1
2
2 2
样例输出1
1
样例输入2
3
3 2 1
样例输出2
2
分析:单调递减栈,需要特别考虑相同时的情况
#include<bits/stdc++.h>
using namespace std;
long long n,same;
long long a[500100];
long long ans;
stack< long long >s;
int main()
{
freopen("stack.in","r",stdin);
freopen("stack.out","w",stdout);
scanf("%d",&n);
for(long long i=1;i<=n;i++) scanf("%d",&a[i]);
s.push(a[1]);
for(long long i=2;i<=n;i++)
{
if(s.top()>a[i])
{
ans++;
s.push(a[i]);
}
else
{
while(!s.empty() && s.top()<a[i])
{
ans++;
s.pop();
}
same=0;
while(!s.empty() && s.top()==a[i])
{
same++;
ans++;
s.pop();
}
if(!s.empty()) ans++;
for(long long j=1;j<=same;j++) s.push(a[i]);
s.push(a[i]);
}
}
cout<<ans<<endl;
return 0;
}