H - Somen Nagashi Editorial by sounansya

セグメント木を用いる解法

セグメント木を用いる解法を紹介します。

\(\text{seg}[k]\) を人 \(k\) が戻ってくる時刻 (ただしこの値が現在時刻以下であれば既に列に並んでいるとする) とします。ただし、はじめ全ての人が列に並んでいるので初期値は \(\text{seg}[k]=0\) とします。この \(\text{seg}[k]\) を元に各出来事を処理していくことを考えます。

各出来事でそうめんを得る人の番号は \(\text{seg}[k] \le T_i\) を満たす最小の \(k\) です。これはセグメント木上の二分探索を行うことで \(O(\log N)\) で求めることができます。 人 \(k\) が一旦列から離れることは \(\text{seg}[k]\)\(T_i+S_i\) に更新することで表現できます。この操作も \(O(\log N)\) で行うことができます。

以上でこの問題を解くことができます。計算量は \(O(N+M\log N)\) です。

実装例 (C++)

#include <bits/stdc++.h>
#include <atcoder/all>
using namespace std;
int op(int a, int  b) { return min(a, b); }
int inf() { return 1 << 30; }
int jud;
bool f(int x) { return x > jud; }
int main() {
	int n, m;
	cin >> n >> m;
	vector<int> a(n);
	atcoder::segtree<int, op, inf> seg(a);
	vector<long long> ans(n);
	for(int i = 0; i < m; i++) {
		int t, w, s;
		cin >> t >> w >> s;
		jud = t;
		int idx = seg.max_right(0, f);
		if (idx == n) continue;
		ans[idx] += w;
		seg.set(idx, t + s);
	}
	for (long long i : ans) cout << i << '\n';
	return 0;
}

posted:
last update: