题意
现在要求模拟一个文档编辑,有5种操作,I x:在光标后面插入一个数字x,且光标后以一位,相当于输入;D:删除光标前面的一个数字,相当于backpack;L:将光标向左移动一位;R:光标向右移动一位;Q x:查询前x个数字的最大前缀和,保证x在光标位置之前。
分析
由于每次操作都是在光标所在的位置,而且只改变一位。所以我们可以用光标模拟栈顶指针,将光标左边的数字,也就是有效数字存进一个栈中。对于I操作,相当于将x压入栈,对于D操作相当于pop栈顶元素。最后的查询是要求最大的前缀和,而且他保证了查询的位置一定是在光标前,所以我们可以对光标前的数字,也就是栈中的数字建立前缀数组sum,并且用另一个数组dp记录从开始到当前位置中的最大前缀和,那么对于Q操作直接输出dp[x]即可。
还有剩下两个操作,左移和右移。因为左移以后的数字并不是被删除了,当右移的时候还需要重新利用,这个时候我们模拟下可以发现,右移的这个操作可以看成是另一个栈,每当右移的时候将原来栈顶的元素放在另一个栈中,然后左移的时候将另个栈的栈顶元素重新压回来。
最后说下sum数组和dp数组是如何建立的,因为要存的是栈中的数字,所以每当有入栈操作的时候就要计算前缀和以及更新dp数组。也就是I操作和R操作。
代码中还要注意一点的是,如果当前光标已经是第一个了,那么再往左边移动也是不变的,也就相当于,第一个栈的元素如果是空的话,是无法出栈的。同理当光标是最后一个位置的时候右移也是无法移动的,这相当于第二个栈如果是空的话,也是无法出栈的。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int MAXN = 1e6;
ll sum[MAXN + 5], dp[MAXN + 5];
int main()
{
ios::sync_with_stdio(false);
int n;
while (cin >> n)
{
memset(sum, 0, sizeof(sum));
memset(dp, 0, sizeof(dp));
dp[0] = -10000000000000000;
string op;
int x;
stack<int> a, b;
while (n--)
{
cin >> op;
if (op[0] == 'I')
{
cin >> x;
a.push(x);
int pos = a.size();
sum[pos] = sum[pos - 1] + x;
dp[pos] = max(dp[pos - 1], sum[pos]);
}
else if (op[0] == 'D')
{
a.pop();
}
else if (op[0] == 'L')
{
if (a.size())
{
x = a.top();
a.pop();
b.push(x);
}
}
else if (op[0] == 'R')
{
if (b.size())
{
x = b.top();
b.pop();
a.push(x);
int pos = a.size();
sum[pos] = sum[pos - 1] + x;
dp[pos] = max(dp[pos - 1], sum[pos]);
}
}
else
{
cin >> x;
cout << dp[x] << endl;
}
}
}
return 0;
}