QUPC2018-E:Treeone

最近よく見るやつ。

問題概要

長さNの数列\{A_i\}が与えられる。その数列の連続する部分列であって,総和が0であるものの個数を数列のたぴさと呼ぶ。数列のうち1つの値を自由に変えられるとき,あり得るたぴさの最小値を求めよ。
E - Treeone

解法

「数列の連続する部分列であって,総和が0であるものの個数」と聞いたら,累積和\{S_i\}を考えるのが定石である。その個数は,S_i=S_jとなる(i,j) (i\leqq j)の組の個数に等しい。つまりは,ある条件を満たす区間の数である。このうち,A_kの値を一つだけ変えて,どれだけそういう区間を減らせるかということを考えよう。答えは

  • 「元の数列のたぴさ」から「A_kを変えることで減らせる区間の数の最大値」を引いたもの

である。「元の数列のたぴさ」の効率的な計算については,A - Zero-Sum Rangesがそのものなので参照していただきたい。
A_kの値をうまく変えることによって,i\leqq k \leqq jを満たす区間(i,j)については,条件を満たさないようにできる。例えば,A_kを5000兆とかにすれば,もともと和が0だった区間の和が5000兆くらいになる。また,A_kを5000兆に変えたことによって,和が0でなかったの区間の和が0になることもない。kを含む区間については5000兆という数字がでかすぎて和が0になりようがないし,kを含まない区間A_kが5000兆になったところで何の影響もない。よって,各kについて,「i\leqq k \leqq jを満たす区間(i,j)であって,S_i=S_jなるものの個数」が効率的に求まればよい。
これにはimos法と呼ばれるものを使う(imos法を知らない方は調べてください)。各iについて,「iから始まる条件をみたす区間の数」足し算し,「iで終わる条件をみたす区間の数」を引き算する。その後,順に累積和を取っていけば,各kに対して,「kを含む条件をみたす区間の数」がわかる。また

  • iから始まる条件をみたす区間の数」は「i\leqq jであって,S_i=S_jをみたすものの個数」に等しい
  • iで終わる条件をみたす区間の数」は「j\leqq iであって,S_j=S_iをみたすものの個数」に等しい

よって,各S_iの値が全部でいくつあるかを調べておき,iが小さい順に上の計算をしてけばよい(詳細はコードを見た方がわかると思う)。実装上注意すべきは,単にS_i=0となるような場合(つまり,A_1+\cdots +A_i=0となる場合)を数えるために,S_0=0から計算を行うところである。

提出

https://beta.atcoder.jp/contests/qupc2018/submissions/3441083

  • S_iがどんな値かわからないため,mapを用いている
  • i\leqq jであって,S_i=S_jをみたすものの個数」や「j\leqq iであって,S_j=S_iをみたすものの個数」は適当に引き算すれば求まるので,直接調べる必要はない


方針はすぐだったが,変なところでバグらせて足を引っ張ってしまった。反省。