リアルタイム受験した。4時間15分で全完。
J - 長い長い文字列
各操作で文字列の長さがどうなるかは先頭からシミュレーションすればわかるので、事前に求めておく。(大きな数になりえるので、適当に上限を決めてを取る。)
番目の操作で長さがになるとする。列で以上である最小の位置を求めて、その位置にアルファベットがあればそれが答えである。数字の場合は、この操作を行う前の長さをとするとき、文字目は文字目と一致しているはずである。したがって、として同じことを繰り返せばよい。ただし、文字目は文字目と考える。
L - T消し
TDPCのiwiを思い出して見に行ったらそのものだった。
M - 棒の出荷
最小値を決め打って判定問題を解く。位置で区切れるならtrue、不可能ならfalseというDPを考えると、適当な区間についてという遷移になる。コンテスト中はすぐにセグ木を貼ったが、累積和上でDPするとか、(はについて単調なので)スライドさせながらDPするとか、なんでもできそう。
O - 通知
愚直に処理すると、次数の大きい頂点が何回も更新されるときに困るが、次数の大きい頂点はそんなに多くなりえないことに注目する。閾値を決めてそれより次数の大きい頂点とそうでない頂点をわけて処理してみる。以下、次数がより大きい頂点をlarge node、小さい頂点をsmall nodeと呼ぶことにする。
通知クエリを処理するとき、small nodeからは周囲に愚直に通知を送り(small nodeなので送るべき頂点は多くない)、large nodeからは送らずに自身にためておいて、消化クエリが来た時に受け取る側から読みに行きたい(large nodeは多くないので毎回全部調べてもよい)。そこで、3つの配列を管理する:
- small nodeからに送られた通知で未読のものの総数
- large nodeであるが周囲に送った通知の総数
- 頂点 がlarge node から受け取った通知で既読のものの総数。
具体的な処理は次のようになる:
- に対する通知クエリ
- がsmall nodeなら、に接続しているすべての頂点について、する。
- がlarge nodeなら、する。
- に対する消化クエリ
- small nodeからの通知の総数を得る: を読んだあと、にリセットする
- large nodeからの通知の総数を得る: に接続しているすべてのlarge node について、から既読の通知の総数を引いたものの総和を取る。として、既読を更新しておくこと。
計算量について考える。辺が1つ増えると全体の次数が2増えるので、次数の総和はである。したがって、次数以上の頂点、つまりlarge nodeは、高々個しかない。クエリあたりの計算量は以下のように評価できる:
- に対する通知クエリ
- がsmall nodeなら
- がlarge nodeなら
- に対する消化クエリ
- small nodeからの通知の合計を得るのは
- large nodeからの通知の合計を得るのは
結局、全体の計算量はと書けて、なら)。
解説の実装はを頂点がlarge nodeから今までに受け取った通知の数の合計として一次元配列にまとめていた。確かにそれでよかった。