一,50分
不用说,直接上代码:
#include <bits/stdc++.h>
using namespace std;
long long mei,x,y,op,tim,n,now,m[10000001];
int main()
{
scanf("%lld",&n);//n次操作
for(int i = 0; i < n; i++)
{
scanf("%lld",&op);//本次操作是哪一种
if(op == 1)//第一种
{
scanf("%lld%lld",&x,&y);
now += x;//现在的时间段+跨越x的时间
mei += y;//吃掉一个美味度为y的羊腿
m[now] = mei;//m[i]代表时间段为i的美味度
}
else
{
cin>>x;
for(int j = now - x; j >= 0; j--)//通过枚举找到最后一个<= x - y 的吃完羊腿的时间点
if(m[j] != 0 || j == 0)//如果不存在这样的时间点,则回到时间点0
{
tim = j;
break;
}
mei = m[tim];
}
printf("%lld\n",mei);
}
return 0;
}
原因:在这里 ,而m[]数组的下标就是目前
,而如果把m数组开成10^18会爆空间,所以这种方法不行。
二,100分
考虑记录两个数组 time[] 和 num[] 分别表示第 i 件事情结束后的 时间和总美味度。 显然操作 1 可以直接加入,num 做一个前缀和即可;操作 2 只需二分答案找到最近的在需要 的时间之前的时间点即可,时间复杂度 O(n log n)。 但是此题也可以进一步优化。考虑每次回退操作直接从后往前依次模拟当前时间是否在需要的 时间之前,可以发现每一个事件最多进栈一次出栈一次,时间复杂度 O(n)。
代码:
#include <bits/stdc++.h>
using namespace std;
long long ti[2000010],sum[2000010],m,now,op, x, y;
int main()
{
cin>>m;
for(int i = 1; i <= m; i++)
{
scanf("%lld%lld",&op,&x);
if(op == 1)
{
scanf("%lld",&y);
now++;
ti[now] = ti[now - 1] + x;//前缀和
sum[now] = sum[now - 1] + y;
}
else
{
int t = ti[now];
if(ti[now] <= x) now = 0;
else
while(ti[now] > t - x)//二分答案
now--;
}
cout<<sum[now]<<endl;
}
return 0;
}