ABC413 C Large Queue 题解
本题做法
- 队列(
queue
)和结构体。
思路
我们一开始最容易想到的方法就是使用一个动态数组 vector
来存储数据,但是这样操作光是一次操作 1 的 \(c\) 达到极限数据 \(10^9\) 就会直接超时,时间复杂度为 \(O(c)\)。我们需要想一些优化思路。
定义结构体类型 node
代表一次操作 1 所存储的一组数据,其中包含 3 个成员变量 \(n,x\) 和 \(s\),分别代表这组数据内的数据个数(就是输入的 \(c\)),数据大小(就是输入的 \(x\))和这组数据的总和(即 \(c\cdot x\))。
定义一个队列 \(Q\) 用来存储输入的多组数据,初始为空队列。每次操作 1,把当前数据的 \(n,x\) 和 \(s\) 计算好放到队尾。这样我们就成功把操作 1 的时间复杂度从 \(O(c)\) 优化到了 \(O(1)\)!
那么怎么优化操作 2 呢?因为队列的特性是 “FIFO 先进先出”,所以我们只要不断地访问队首并弹出队首就可以做到模拟弹出并累加数组的前 \(k\) 个元素的效果。
使用变量 \(t\) 和 \(sum\) 分别存储当前已经弹出的数据个数和它们的累加和。每一次访问队首的一组数据,首先将 \(t\) 加上队首数据的 \(n\),然后分类讨论:
- 若 \(t\le k\),则代表刚好累加了 \(k\) 个数据或者还不够。此时就要先将 \(sum\) 加上这组数据的 \(s\),然后再将队首的这组数据整组弹出(即
Q.pop();
)。 - 若 \(t>k\),则代表多加了一些数据,记多加的数量为 \(m=t-k\),那么我们既然多加了,就需要还原回去。先将 \(sum\) 加上加了的数据的总和 \(x(n-m)\),然后将队首那组数据的 \(n\) 减去加了的(即减去 \(n-m\)),最后将队首数据的 \(s\) 重新赋值为当前的 \(nx\)。
重复以上操作,直到 \(t\ge k\) 为止,然后输出累加的和 \(sum\)。如此一来,我们又成功把操作 2 的时间复杂度优化到了同操作 1 的次数一样多(因为操作 2 最多弹出的数据组数为操作 1 的次数组)!
经过一通优化,我们的代码终于可以 AC 了!
代码
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const double EPS=1e-8;
struct node{
long long n;
long long x;
long long s;
};
int Q;
deque<node> q;
int main(){
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin>>Q;
while(Q--){
int op;
cin>>op;
if(op==1){
long long x,c;
cin>>c>>x;
q.push_back({c,x,c*x});
}else{
long long k;
cin>>k;
long long t=0,s=0;
while(t<k&&!q.empty()){
t+=q.front().n;
if(t<=k){
s+=q.front().s;
q.pop_front();
}else{
s+=(q.front().n-(t-k))*q.front().x;
q.front().n-=q.front().n-(t-k);
q.front().s=q.front().n*q.front().x;
}
}
cout<<s<<endl;
}
}
return 0;
}