Querying Multiset [AtCoder] [abc212] [D]

题目:D - Querying Multiset

Time Limit: 2 sec / Memory Limit: 1024 MB
https://atcoder.jp/contests/abc212/tasks/abc212_d

Problem Statement/问题陈述

Takahashi has many balls, on which nothing is written, and one bag. Initially, the bag is empty. Takahashi will do Q operations, each of which is of one of the following three types.

Type 1: Write an integer Xi on a blank ball and put it in the bag.
Type 2: For each ball in the bag, replace the integer written on it with that integer plus Xi
Type 3: Pick up the ball with the smallest integer in the bag (if there are multiple such balls, pick up one of them). Record the integer written on this ball and throw it away.
For each 1iQ, you are given the type Pi of the i-th operation and the value of Xi if the operation is of Type 1 or 2. Print the integers recorded in the operations of Type 3 in order.

翻译:
高桥有很多空白的球和一个袋子。最初,袋子是空的。高桥将会作出Q个操作,具体如下:
操作1 在白球上写一个数字Xi然后扔进袋子里;
操作2 将袋子里所有球的数字都加上Xi;
操作3 输出袋子里最小的数字并把它从袋子里取出。

Constraints/数据范围

在这里插入图片描述

Input/输入格式

在这里插入图片描述

Output/输出格式

在这里插入图片描述

样例:

输入:
5
1 3
1 5
3
2 2
3

输出:
3
7

说明:
Write 3 on a ball and put it in the bag.
Write 5 on a ball and put it in the bag.
The bag now contains a ball with 3 and another with 5. Pick up the ball with the smaller of them, or 3. Record 3 and throw it away.
The bag now contains just a ball with 5.Replace this integer with 5+2=7.
The bag now contains just a ball with 7. Pick up this ball, record 7, and throw it away.

解:

显然直接模拟会超时,但这个题也没必要写个线段树。所以我们可以借鉴一下lazyTag的思想。
这个思路也是我看tourist的代码学来的。

每次操作2只会影响之前入袋的球,不会影响之后的球。

那我们不妨让球入袋时先被之前所有的操作2先“逆影响”一下,到出袋的时候再“影响”一下,这样加在这个球上的数就只有它入袋到出袋这段时间的所有操作2了。

这本质上是一种时间上的差分前缀和。具体请看代码。

相比于直接模拟,就不必在每次操作2上傻乎乎的把整个袋子里无数的球重新赋值了,因此时间能节省不少。并且因为袋中的元素只需要增加和删除,所以可以用multiset来存储,自动维持有序,连排序都省了。

#include <bits/stdc++.h>
using namespace std;
 
multiset<long long> s;
int q,op;
long long x,delta;
//x是输入输出用的,delta是全局的前缀和

int main() {
  cin >> q;
  while (q--) {
    cin >> op;
    if (op == 1) {
      cin >> x;
      s.insert(x - delta);	//执行操作1时把数字插入multiset,先减去当前的前缀和
    }
    if (op == 2) {
      cin >> x;	
      delta += x;			//要执行操作2,直接在全局的前缀和上加上x
    }
    if (op == 3) {
      x = *s.begin();		//取出当前的最小值
      s.erase(s.begin());   //从multiset里去除
      cout << x + delta << '\n';	//加上现在的前缀和,相当于在这个球的生命周期作了一个差分。
    }
  }
  return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值