Link-cut treeで部分木になんか(sumとか)載せるやつを先に知っておくことを推奨します ( https://beet-aizu.hatenablog.com/entry/2019/06/08/221833 )
上の記事の更に読者層が限定されたバージョンです
さて、部分木にaddとかやりたくないですか ? やりたいですよね ???
Toptree使えばなんでもできるっていうのは置いといてLCTで頑張ってみます
扱えるのは遅延セグ木に乗るような作用素の要件に加えて作用素に逆元があって打ち消しが可能なやつです(例えばaddはこれを満たします)
遅延セグ木のノリで遅延を部分木に流そうとするとたくさん生えてるlight edge全部見ることになって多分大変なことになります
これは親からたくさんある子に一気に流すのが悪いので、代わりに必要なときに子から親の遅延情報を引っ張ってくる感じで伝播させます。
具体的にはsubtree addでは
-
heavy edgeへもlight edgeへも同じ遅延情報を使う
-
各ノードは変数
added
,cancel
,sum
を持つ(それぞれ初期値0) -
subtree addするときはcutしてexposeしてそのノードの
added
に足したい分だけ足す(その後cutしたのを繋ぎなおす) -
added
の伝播
ノードx
が親から遅延情報取ってくるときx->added += x->parent->added - x->cancel
x->sum += x->size * (x->parent->added - x->cancel)
x->cancel = x->parent->added
をします。
たくさんあるlight edgeで繋がった子から違うタイミングで使われるのでadded
は絶対にクリアされません。
ただそれだと同じ遅延情報を2回3回引っ張ってきてしまうのでcancel
に「最後に親から取ってきたときの親のadded
」を入れておくことで差分だけ取ってくるようにします。
親が変わるときは適切にx->cancel
をx->parent->added
に合わせる必要があります。(特にrotateとかでheavy edgeをいじるだけのときもやります)
また、必然的に(「ノードx
の遅延情報はこれからノードx
に適用するもの」方式に対して)「ノードx
の遅延情報はノードx
に既に適用されている」方式を取ることになります。
コードは... よすぽさんのgithub
このコードではevertのreverse遅延は「ノードx
の遅延情報はこれからノードx
に適用するもの」方式をとっています。紛らわしいですね
なお一箇所オーバーフローさせてhotmanさん困らせてましたごめんなさい。