Official
D - All Assign Point Add Editorial by MMNMM
最初にすべての要素に \(0\) が代入されたと考えて、最後の代入からの差分を保持することを考えます。 与えられるクエリは次のように言い換えられます。
- これまでの差分をすべて消して \(0\) にし、最後に代入された値を \(x\) に変更する。
- \(i\) 番目の差分に \(x\) を加える。
- 最後に代入された値に \(i\) 番目の差分を加え、出力する。
1番目のクエリで差分の値が変わる場所の個数は、直前の代入以降の2番目のクエリの個数を超えません。 よって、「差分が \(0\) でない箇所」を持っておくことで、全体で \(O(N+Q)\) 時間でこの問題を解くことができます。
また、長さ \(N\) の列に区間更新・一点取得を行うセグメント木があれば、それを直接用いることで \(O(N+Q\log N)\) 時間でこの問題を解くことができます(実装例)。
実装例は以下のようになります。
#include <iostream>
#include <vector>
#include <numeric>
int main() {
using namespace std;
unsigned N;
cin >> N;
// 列を base からの差分で管理
vector<unsigned long> A(N);
unsigned base{};
for (auto &&a : A)cin >> a;
A.emplace(begin(A));
// 差分が 0 でないかもしれない場所
// はじめは 1, ..., N すべて
vector<unsigned> added_index(N);
iota(begin(added_index), end(added_index), 1U);
unsigned Q;
cin >> Q;
for (unsigned _{}; _ < Q; ++_) {
unsigned type;
cin >> type;
if (type == 1) {
// 差分が 0 でないところを消去
while (!empty(added_index)) {
A[added_index.back()] = 0;
added_index.pop_back();
}
cin >> base;
} else if (type == 2) {
// index 番目の差分に加える
unsigned index, value;
cin >> index >> value;
A[index] += value;
added_index.emplace_back(index);
} else {
// base に index 番目の差分を加える
unsigned index;
cin >> index;
cout << base + A[index] << endl;
}
}
return 0;
}
posted:
last update: