2021年3月4日木曜日

JOI2021 本選 C - 集合写真

以下、すべて0-basedに直して考えている。

最終形は2104387652 1 0 |4 3|8 7 6 5のように、0,1,...,N10, 1, ..., N-1をいくつかの連続部分列に区切って各区間を反転させたものになっている。

dp[x]=\operatorname{dp}[x] = 先頭からxx個の階段に0,1,...,x10, 1, ..., x-1を有効な順番で並べる場合の最小交換数

としてDPができそう。

DPの遷移を考える。位置0,1,...,x10, 1, ..., x-1に人0,1,...,x10, 1, ..., x-1を配置済みとして、位置xxからx,x+1,...,y1x, x+1, ..., y-1を逆順に並べる場合のコストをf[x][y]f[x][y]とすると、dp[y]max(dp[y],dp[x]+f[x][y])\operatorname{dp}[y] \leftarrow \max(\operatorname{dp[y]}, \operatorname{dp}[x] + f[x][y])のように配るDPがO(N2){O}(N^2)でできる。

次にffの求め方を考える。与えられた列の中に散らばっている数x,x+1,...,y1x, x+1, ..., y-1を位置x,x+1,...,y1x, x+1, ..., y-1に逆順で整列させる時、c[i][j]:=c[i][j] :=iiの左にあるjj以上の数の総数として、だいたいc[x][x]+c[x+1][x]+...+c[y1][x]c[x][x] + c[x+1][x] + ... + c[y-1][x]回の交換をしてx,x+1,...,y1x, x+1, ..., y-1を前のほうに持ってくることになるが、この際、与えられた列上でのx,x+1,...,y1x, x+1, ..., y-1の順番を考えた時に、既に転倒が起きているぶんの交換はしなくてよい。そこで、inv[x][y]:=\operatorname{inv}[x][y] := 与えられた列からx,x+1,...,y1x, x+1, ..., y-1を抜き出した部分列の転倒数、とすると

f[x][y]=c[x][x]+...+c[y1][x]inv[x][y] f[x][y] = c[x][x] + ... + c[y-1][x] - \operatorname{inv}[x][y]

と求まる。ccは累積和と同じように構成できて、さらにその累積和C[x][y]:=c[0][y]+...+c[x1][y]C[x][y] := c[0][y] + ... + c[x-1][y]を取っておけば

f[x][y]=C[y][x]C[x][x]inv[x][y] f[x][y] = C[y][x] - C[x][x] - \operatorname{inv}[x][y]

と求まる。

最後にinv\operatorname{inv}の作り方を考える。ここではinv[x][y1]\operatorname{inv}[x][y-1]inv[x][y]\operatorname{inv}[x][y]の差分、つまり、与えられた列上のx,x+1,...,y2x, x+1, ..., y-2に対応する部分列に(与えられた順番通りに)y1y-1を挿入した時の転倒数の増分を考える。この増分は与えられた列上でy1y-1の後ろにあって[x,y1)[x, y-1)に含まれる数の総数に等しい。y1y-1の後ろよりも前にある数のほうが(ccを使って)表しやすいので、さらにこれを(y1x)(y-1-x) -(y1y-1の前にあって[x,y1)[x, y-1)に含まれる数の総数)と言い換える。この増分は上で求めたccを使って表せて、結局

inv[x][y]inv[x][y1]+(y1x)(c[y1][x]c[y1][y]) \operatorname{inv}[x][y] \leftarrow \operatorname{inv}[x][y-1] + (y-1-x) - (c[y-1][x] - c[y-1][y])

と遷移できる。

これで、全体としてO(N2){O}(N^2)で計算できたことになる。