栈
描述:栈是一种“后入先出”的数据结构。
支持操作:入栈(push),出栈(pop);
单调栈:借助单调性处理问题,即使排除不可能的选项;
单调栈的应用: 对于可以转化某一元素为找到其右边第一个大于(小于等于)的元素的题目,可以利用单调栈的单调性优化求解.
例题讲解:
在某个字符串(长度不超过100)中有左括号、右括号和大小写字母;规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。写一个程序,找到无法匹配的左括号和右括号,输出原来字符串,并在下一行标出不能匹配的括号。不能匹配的左括号用"$“标注,不能匹配的右括号用”?"标注.
分析:忽略大小写字母的输入,从左往右一个个读取字符,当字符为左括号时进栈,右括号时将栈顶元素出栈,若栈顶为空则不能匹配,运用栈这种数据结构即可解决此题;
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
char s[105],ans[105];
int t[105];
int main()
{
while(~scanf("%s",s+1))
{
int len = strlen(s+1),tot = 0;
for(int i = 1;i <= len;++i)
{
if(s[i]=='(')
t[++tot] = i;
else if(s[i]==')'&&tot)
ans[i] = ans[t[tot--]] = ' ';
else if(s[i]==')') ans[i] = '?';
else ans[i] = ' ';
}
printf("%s\n",s+1);
for(int i = 1;i <= tot;++i) ans[t[i]] = '$';
for(int i = 1;i <= len;++i) printf("%c",ans[i]);
cout << endl;
}
return 0;
}
例题:
分析: 单调栈基本题目;
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll h[100005],tot,w[100005];
int main()
{
int n;cin >> n;
while(n)
{
tot = 0;h[tot] = -1;
ll ans = -1;
for(int i = 0;i <= n;++i) w[i] = 1;
for(int i = 1;i <= n;++i)
{
ll t,cnt = 0;scanf("%lld",&t);
while(t<=h[tot])
{
cnt += w[tot];
ans = max(ans,h[tot--]*cnt);
}
h[++tot] = t;
w[tot] = cnt+1;
}
ll cnt = 0;
while(tot)
{
cnt += w[tot];
ans = max(ans,h[tot--]*cnt);
}
cout << ans << endl;
scanf("%d",&n);
}
return 0;
}
队列:
描述:一种先进先出的数据结构;
例题:
蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓。
本题中,我们将用符号 ⌊c⌋ 表示对 c 向下取整,例如:⌊3.0⌋=⌊3.1⌋=⌊3.9⌋=3。
蛐蛐国里现在共有 n 只蚯蚓(n 为正整数)。每只蚯蚓拥有长度,我们设第 i 只蚯蚓的长度为 ai(i=1,2,…,n),并保证所有的长度都是非负整数(即:可能存在长度为 0 的蚯蚓)。
每一秒,神刀手会在所有的蚯蚓中,准确地找到最长的那一只(如有多个则任选一个)将其切成两半。神刀手切开蚯蚓的位置由常数 p(是满足 0<p<1 的有理数)决定,设这只蚯蚓长度为 x,神刀手会将其切成两只长度分别为 ⌊px⌋ 和 x−⌊px⌋ 的蚯蚓。特殊地,如果这两个数的其中一个等于 0,则这个长度为 0 的蚯蚓也会被保留。此外,除了刚刚产生的两只新蚯蚓,其余蚯蚓的长度都会增加 q(是一个非负整常数)。
蛐蛐国王知道这样不是长久之计,因为蚯蚓不仅会越来越多,还会越来越长。蛐蛐国王决定求助于一位有着洪荒之力的神秘人物,但是救兵还需要 m 秒才能到来 ……(m 为非负整数)
蛐蛐国王希望知道这 m 秒内的战况。具体来说,他希望知道:
m 秒内,每一秒被切断的蚯蚓被切断前的长度(有 m 个数);
m 秒后,所有蚯蚓的长度(有 n+m 个数)。
蛐蛐国王当然知道怎么做啦!但是他想考考你 ……
分析:对于整体加的操作我们可以替换为单体减的操作,用del表示数据的值与真实值差据,通过建立三个队列即可解题;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
queue<ll>q1,q2,q3;
ll a[100005];
int main()
{
int n,m,q,u,v,t;scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
for(int i = 1;i <= n;i++)scanf("%lld",&a[i]);
sort(a + 1,a + 1 + n);
for(int i = n;i >= 1;i--)q1.push(a[i]);
ll delta = 0;
for(int i = 1;i <= m;i++)
{
ll mx = -1e9;
int w = 0;
if(q1.size() && mx < q1.front())
{
w = 1;
mx = q1.front();
}
if(q2.size() && mx < q2.front())
{
w = 2;
mx = q2.front();
}
if(q3.size() && mx < q3.front())
{
w = 3;
mx = q3.front();
}
if(w == 1)q1.pop();
if(w == 2)q2.pop();
if(w == 3)q3.pop();
mx = mx + delta;
if(i % t == 0)
{
printf("%lld ",mx);
}
ll x1 = mx * u / v - delta - q,x2 = mx - mx * u / v - delta - q;
q2.push(x1);q3.push(x2);
delta += q;
}
printf("\n");
for(int i = 1;i <= m + n;i++)
{
ll mx = -1e9;
int w = 0;
if(q1.size() && mx < q1.front())
{
w = 1;
mx = q1.front();
}
if(q2.size() && mx < q2.front())
{
w = 2;
mx = q2.front();
}
if(q3.size() && mx < q3.front())
{
w = 3;
mx = q3.front();
}
if(w == 1)q1.pop();
if(w == 2)q2.pop();
if(w == 3)q3.pop();
if(i % t == 0)
{
printf("%lld ",mx + delta);
}
}
return 0;
}
方块:通过适当的划分,预处理一部分信息并保存下来,用空间换取时间,达到时空平衡;
使用:通常将一个数组划分为sqrt(N)个;
例题:
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的元素个数。
分析:分块解决,用L代表块的左端,R代表块的右端,对块内部进行排序可以快速得到答案;
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
const ll siz = 1e5;
ll a[siz],add[siz],b[siz];
int L[siz],R[siz],pos[siz],n;
void div()
{
int q = sqrt(n);
for(int i = 1;i <= q;++i)
{
L[i] = (i-1)*q+1;
R[i] = i*q;
add[i] = 0;
sort(b+L[i],b+R[i]+1);
for(int j = L[i];j <= R[i];++j) pos[j] = i;
}
if(R[q]!=n)
{
L[q+1] = R[q]+1;
R[q+1] = n;
add[q+1] = 0;
sort(b+L[q+1],b+n+1);
for(int i = R[q]+1;i <= n;++i) pos[i] = q+1;
}
}
void ad(int l,int r,ll d)
{
int p = pos[l],q = pos[r];
if(p^q)
{
for(int i = l;i <= R[p];++i) a[i] += d;
for(int i = L[p];i <= R[p];++i) b[i] = a[i];
sort(b+L[p],b+R[p]+1);
for(int i = p+1;i < q;++i) add[i] += d;
for(int i = L[q];i <= r;++i) a[i] += d;
for(int i = L[q];i <= R[q];++i) b[i] = a[i];
sort(b+L[q],b+R[q]+1);
}
else
{
for(int i = l;i <= r;++i) a[i] += d;
for(int i = L[p];i <= R[p];++i) b[i] = a[i];
sort(b+L[p],b+R[p]+1);
}
//for(int i = 1;i <= n;++i) printf("%lld ",b[i]+add[pos[i]]);cout << endl;
}
ll query(int l,int r,ll c)
{
int p = pos[l],q = pos[r];ll ans=0;
if(p^q)
{
for(int i = l;i <= R[p];++i) if(a[i]+add[p]<c) ++ans;
for(int i = p+1;i < q;++i) ans += lower_bound(b+L[i],b+R[i]+1,c-add[i])-b-L[i];
for(int i = L[q];i <= r;++i) if(a[i]+add[q]<c) ++ans;
}
else for(int i = l;i <= r;++i) if(a[i]+add[p]<c) ++ans;
return ans;
}
int main()
{
cin >> n;
for(int i = 1;i <= n;++i)
{
scanf("%lld",a+i);
b[i] = a[i];
}
div();
for(int t = 1;t <= n;++t)
{
int op,l,r;ll c;scanf("%d%d%d%lld",&op,&l,&r,&c);
if(op)
{
ll ans = query(l,r,c*c);
printf("%lld\n",ans);
}
else
ad(l,r,c);
}
return 0;
}