2019年12月16日月曜日

WUPC 2019 C - Choose Your Characters

勝てない相手キャラがいないような最短の区間を尺取り法で求める。つまり、dp[i]:=\operatorname{dp}[i] :=キャラiiが勝てるキャラの数、として各区間について正しいdp\operatorname{dp}を求めて、minidp[i]>0\min_i \operatorname{dp}[i] >0になるような最短の区間を探せばよい。

具体的には、最初に

S(i):=S(i) := キャラiiが勝てないキャラの集合(同キャラ含む)

と言うデータを作っておく。区間を伸ばすときはdp\operatorname{dp}全体に1を足してから各kS(i)k \in S(i)についてdp[k]=1\operatorname{dp}[k] \mathrel{{-}{=}} 1とし、区間を縮めるときにはdp\operatorname{dp}全体から1を引いてから各kS(i)k \in S(i)についてdp[k]+=1\operatorname{dp}[k] \mathrel{{+}{=}} 1とする。dp\operatorname{dp}にセグメント木等を使えば簡単。


ようやく尺取り法の間違えにくい書き方にたどり着いたのでメモ。

  • ok関数とng関数で相互再帰する。
  • 常に半開区間[l,r)[l, r)を扱う。(ok l r)[l,r)[l, r)が条件を満たすときに呼び出され、(ng l r)[l,r)[l, r)が条件を満たさないときに呼ばれる。rはこれから追加される位置であり、lはこれから除去される位置である。
  • (ok l r value)のように区間に関する値を受け渡す場合、valueは常に区間[l,r)[l ,r)に対応する値になるようにする。
  • 最適値を求める場合はok関数の冒頭で更新する。