1.题目大意:文本编译光标。有如下操作:(1)每次在光标后插入一个元素,光标随即移动到序列尾部(2)在光标前面有元素的情况下删除光标的前一个元素(三)如果光标不在序列头部就将光标左移(四)如果光标不在序列尾部就将光标右移(五)查询当前光标前的最大前缀和
2.第一眼想到,用数组维护所有元素的前缀和和最大前缀和,使用双向链表维护光标。但是头疼的是光标并不指向元素,而是在元素中间,类似电脑鼠标的光标。而且双向链表写起来很麻烦。虽然链表是数据结构的基础,但是实际上能不写链表就不写链表。这里我第一次了解到栈的强大之处:双栈对弹
3.如下图,两个栈构成了整个序列,初始时如果光标没有移动右栈永远是空的,那么不断插入就不断向左栈push即可,光标如我们所期待那样移动:
如果我们要删除元素,显然直接从左栈删除即可,光标也会如我们所料移动
如果让光标左移,那么将左栈栈顶弹出并push到右栈栈顶:
同理光标右移,光标的变化也如我们期望
4.至于前缀和,两个数组维护即可,每次查询输出当前左栈长度最大前缀和。不难发现我们只要维护左栈长度即可,插入和右栈左弹实现原理一样,更新当前最大前缀和。删除的话sz–即可
5.其实双向链表也没有那么麻烦,用数组模拟双向链表即可,就是容易把人搞晕。但是能多学一个技能还是要多学,这个黑科技确实强大!
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn=1e6+10;
typedef long long ll;
struct Stack{
ll a[maxn];
int cur;
void init(){
cur=0;
memset(a,0,sizeof a);
}
void push(ll x){
a[++cur]=x;
}
void pop(){
if(!empty()) cur--;
}
ll top(){
return a[cur];
}
int size(){
return cur;
}
int empty(){
return !cur;
}
};
ll sum[maxn],Max[maxn];
Stack L,R;
int main()
{
int n,x,sz;
char op;
while(~scanf("%d",&n)){
L.init(),R.init();
memset(sum,0,sizeof sum);
memset(Max,0,sizeof Max);
Max[0]=-INF; //注意!!!
sz=0;
while(n--){
cin>>op;
if(op=='I'){
scanf("%d",&x);
L.push(x);
sz++;
sum[sz]=sum[sz-1]+x;
Max[sz]=max(sum[sz],Max[sz-1]);
}else if(op=='D'){ //和下面两个一样必须判断栈不为空
if(L.size()){
L.pop();
sz--;
}
}else if(op=='L'){
if(L.size()){
R.push(L.top());
L.pop();
sz--;
}
}else if(op=='R'){ //右移时左栈前缀和更新
if(R.size()){
L.push(R.top());
R.pop();
sz++;
sum[sz]=sum[sz-1]+L.top();
Max[sz]=max(sum[sz],Max[sz-1]);
}
}else if(op=='Q'){
scanf("%d",&x);
printf("%lld\n",Max[x]);
}
}
}
return 0;
}