Official

D - Querying Multiset Editorial by mechanicalpenciI


数列 \((A_1,\ldots ,A_N)\)\(i\) 回目の操作が操作 \(2\) ならば \(A_i=X_i\) そうでないならば \(0\) で定めます。このとき \(K+1\) 回目の操作の直前において袋に入っているそれぞれボールに書かれた数は \(X_i+A_{i+1}+\cdots A_K\) と書けます。また、累積和の考え方を使うと、 \(S_i\)\(S_i=A_1+\cdots A_i\) で定めて、これを \(X_i+(S_K-S_i)=(X_i-S_i)+S_K\) と書く事が出来ます。ここで \((A_i)\) および \((S_i)\) は順に見ていく事で簡単に計算ができます。

さて、最小の番号のボールを取り出そうとしたとき 、 \(S_K\) の部分についてはボールによらないので、残っているボールの中で \(X_i-S_i\) が最も小さいものを取り出せば良い事が分かります。 よって、操作 \(1\) の度に \(X_i\) ではなく \(X_i-S_i\) の書かれたボールを入れていることにすれば、操作 \(1\) と操作 \(3\) のみの場合に帰着する事が出来ます。(ただし、出力する際には \(S_K\) を足すことを忘れないようにしてください。)

これは優先度付きキューなどのデータ構造を用いることによって高速に、具体的には \(O(Q\log Q)\) で解くことができます。よってこの問題を解く事が出来ました。

C++による実装例:

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define rep(i, n) for(int i = 0; i < n; ++i)

int main(void) {
	priority_queue<ll, vector<ll>, greater<ll> >pq;
	int q, p;
	ll x;
	ll sum = 0;
	cin >> q;
	rep(qq, q) {
		cin >> p;
		if (p == 1) {
			cin >> x;
			pq.push(x - sum);
		}
		else if (p == 2) {
			cin >> x;
			sum += x;
		}
		else if (p == 3) {
			x = pq.top();
			cout << x + sum << endl;
			pq.pop();
		}
	}
	return 0;
}

posted:
last update: