2019年11月13日水曜日

JAG Summer Camp 2013 Day 2 G - Perm Query

与えられた置換ppを巡回置換の合成σ1σ2...σk\sigma_1\sigma_2 ... \sigma_kに分解して、ある巡回置換σ\sigmaに注目する。σ\sigmaの長さがddとすると、

  • σx\sigma xの区間[l,r][l, r]の総和
  • σ2x\sigma^2 xの区間[l,r][l, r]の総和
  • ...
  • σdx=x\sigma^d x = xの区間[l,r][l, r]の総和

をすべて足せばこの巡回置換を一周した場合の総和が求まることになる。この際、巡回置換に現れるすべての数は同じ回数だけ[l,r][l ,r]に現れるので、上の総和は巡回置換に登場する数の総和の倍数になっている。

また、問題のようにすべての巡回置換を同時に適用した場合、次にもとに戻るのは(区間[l,r][l, r]に登場する)すべての巡回置換の長さの最小公倍数である。これをLCM(l,r)\operatorname{LCM}(l, r)とすると、LCM(l,r)\operatorname{LCM}(l, r)回の操作を行う場合、長さddの巡回置換についてはLCM(l,r)/d\operatorname{LCM}(l, r)/d周することになる。この際、巡回置換に登場する数で最初に区間[l,r][l, r]に含まれていたものがqq個であるとすると、巡回置換に登場する数はそれぞれqLCM(l,r)/dq\operatorname{LCM}(l, r)/d回足されることになる。


実装で迷った末に、Moのアルゴリズムを使った。

区間[l,r][l, r]についての値がわかっているとき、[l,r+1][l, r+1]の値を考える。まず、区間が伸びたことによって操作回数がLCM(l,r)\operatorname{LCM}(l, r)からLCM(l,r+1)\operatorname{LCM}(l, r+1)に伸びるので、既存の値がLCM(l,r+1)/LCM(l,r)\operatorname{LCM}(l, r+1)/\operatorname{LCM}(l, r)倍になる。また、位置r+1r+1が追加されるので、この位置を含む巡回置換σ\sigmaの長さddについて、(巡回置換σに登場する数の総和)×LCM(l,r+1)/d(巡回置換 \sigma に登場する数の総和) \times \operatorname{LCM}(l, r+1)/dが加算されることになり、新しい値が求まる。区間が縮む場合も同様に書ける。

LCM(l,r)\operatorname{LCM}(l, r)の値を得るのにsparse tableが使えるから計算量の問題はないと思って書き出したのだが、伸縮の際にmodの割り算が必要になるので結局log\logがついてO(NQlogN){O}(N \sqrt{Q} \log N)だった。[1]


lcmと同時に、それぞれの巡回置換の総和もセグメント木で持てばよいらしい。それはそうか……


  1. lcmの範囲が大きいので逆数のテーブルは用意できない。でも、うまい方法がありそうな気もする。 ↩︎