SRM 638 DIV1 Medium: NarrowPassage2

解法は簡単だが思いつけないタイプ

解法

分割統治法、再帰

 

maxSizeSum=10で色々やってみる。

m = min(size), M = max(size)

とする。

size = {5,2,3,4,6}

のような場合

m = 2, M = 6

であり、

m+M <= maxSizeSumが成り立つので

2はどこにでもおける。

すなわち、2の各位置について、他の要素の並べ方は別々に考えることができる。

つまり

size' = {5,3,4,6}

 の並べ方1通りに対して、2を好きな箇所に挿入でき、挿入できる位置は|size|通り。

これで部分問題

count(size', maxSizeSum)

が得られた。

今度はm+M > maxSizeSum のときを考える。

size = {3,2,5,9,4,6}

とすると

m = 2, M = 9

であり m + M = 11 > maxSizeSum

Mに隣接する値は常にm以上であるはずだから、Mを隣接する値と交換することはできない。つまり、Mの位置は固定された。また、Mより左にある要素はMより右へは行けない。同様にMより右にある要素はMより左へはいけない。なので数列から値がMである要素を一つ取り除いて

left = {3,2,5}, right = {4, 6}

 のようにMの左右の列は別々に並び替えることができる。

よって

部分問題

count(size, maxSizeSum) = count(left, maxSizeSum) * count(right, maxSizeSum)

であり、

もとの問題は部分問題

count(left,maxSizeSum)とcount(right, maxSizeSum)に分割することができた。

最後に再帰の底を考えると、|size|<=1で並べ方は1通りしかない。

思いつきたかった

最小値や最大値が特異な動きをするのは簡単に思いつけそう。少し手を動かして実験をしてみれば、最小値が自由に動ける場合や最大値が固定される場合に気づく?もし最大値が固定される場合に気づけば、その場合は部分問題に分割できることは明らか。そうでない場合を考えてみる。こちらも部分問題に分割できる。分割統治法。あとは再帰の形で表せればいい。

SRM 708 DIV2 Hard: PalindromicSubseq2

dp[i][j]:=左は文字iまで、右側はjまで見たときの偶数長(長さ0以上)の回分の個数の数(ただし1<=i<=j<=N)

たとえば、文字列sが以下のような形の場合(1-indexed)

abcxycba

abc...ba

dp[3][7] = 2^2 = 4

1<=j-i<=nを

nから順に確定していけばいい。つまり

dp[i][j]

= dp[i-1][j] + dp[i][j+1] - dp[i-1][j+1] + (s[i]==s[j]?dp[i-1][j+1]:0)

= dp[i-1][j] + dp[i][j+1] - (s[i]==s[j]?0:dp[i-1][j+1])

f:id:parukii:20170210135639j:plain

SRM 708 DIV1 Easy: BalancedStrings

文字列s[0]~s[N-1]の文字列の末尾に文字追加して解を構成すことを考える。
ある文字列s[i]に文字を追加したときに全体で、instabilityは0~1、similarityは0~100*(N-1)増加する。
このようにinstabilityの値のほうが調整がし易い。
そこでまずsimilarityが小さくなるようにざっくりs[0]~s[N-1]を作ってinstabilityをインクリメントしていく。
similarityを小さくするにはできるだけs[0]~s[N-1]が文字を共有しないでほしいので以下のような簡単な構成が思いつく。
すなわち
s[i] = 'a' + (i mod 26)
この種の構成法について
N=100 の一番面倒な場合についてだけ考えてみる。
i番目の文字列についてj<iでかつs[i] = s[j]であるものはj/26 個ある。
これでsimilarityは数えられた。|s[i]| = 1 よりinstability=0。
similarityに追いつくようinstabilityを増やしていけばよいのだが、similarityの増加の仕方が厄介そうなのは明らか。
similarityが定まったら、similarityを増加させないでinstabilityを調整できれば楽なのだが、そのような構成法はあるだろうか。
ある。実は今まで使っていない文字が2種類以上あるならば、単にその2文字(仮にA,Bとすると)
s[0] + A + B + A + B ... (100文字まで)
のようにAとBを末尾に追加すればよい。
同様に未使用の文字CとDがあるとして
s[1] + C + D + C + D ....(100文字まで)
未使用の文字が必要だったので
s[i]の構成を
s[i] = 'a' + (i mod M) (M<26)に変更しよう。
N=100のときにinstabilityが最大になるのは明らかで、未使用の文字の種類が最大になる。
このときM=22とすると都合がいい。すなわちw,x,y,zの4文字でinstabilityを調整する。
最終的に解は
s[0] = awxwxwxwx...
s[1] = byzyzyzyz...
s[2] = c
s[3] = d
...
のような形になる。

Codeforces #396 Div. 2 E: Mahmoud and a xor trip

とりあえず1を根とする根付き木として考える。

f[i][j][k] := 頂点iについて、頂点iの子孫のうち、iからその子孫までのパスの距離のjビット目がkであるような子孫の数

とする。これをメモ化再帰で解く。

ある頂点uについて

uが最も根に近い頂点であるようなパスは、uと子孫との関係から3パターンがある。

つまりvとwをuの子孫として(u,v,wはすべて異なる)

(1) v <=> u<=> w

(2) u <=> v

(3) u

これらを

f[u][j][k]を計算しながら、uが最も根に近い頂点であるようなパスの距離の和g[u]を求める。Σ(g[u])が解

Codeforces #396 Div. 2 D: Mahmoud and a Dictionary

等しい要素は、素集合データ構造でくっつけていく。

また、互いに素なAとBが反意語であることを示すのに

an[A] = Bかつan[B] = A

のような配列を使う。(unifon find木の各集合を代表する頂点を使えば単なる配列になる。)

あとは問題文に従ってこれらを操作するだけだが、矛盾する場合のチェックはすこし分かりづらかった。以下のとおり。

f:id:parukii:20170209144350j:plain

 

Codeforces #396 Div.2 C: Mahmoud and a Message

解法

まず、[l,r)の部分文字列を作れるかどうか判定するのに

各文字のカウント部分和を計算しておき、

文字iについては

cnt[i][r] - cnt[i][l]

で数えられる。つまり[l,r)の範囲に文字iがあるかどうかわかる。

[l,r)の範囲にあるすべての文字iについて

r-l >= a[i]

が成り立つかチェックする。これで[l,r)の範囲の部分文字列の判定はできた。

あとはこれを使って2種類のDPを解く。

f[x文字目まで見た][直前j個は連続] = 場合の数

g[i文字目まで見た][直前j個は連続] = 部分文字列の数の最小値

文字列全体について、分割の仕方の数と部分文字列の数の最小値は求まった。

最長の部分文字列はDPの更新時についでに計算できる。

SRM 547 DIV2 Hard: RelativelyPrimeSubset

解法

Sの要素が100以下と小さいので100以下の素因数でビットDPを試みたい。しかし、100以下の素数の個数は25個なので

2^25 * |S| <= 2^25*50 = 1.6777216 × 10^9

で間に合わない。

ここでたかだか25個の素数を列挙してみると

2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97

このうち53,59,61,67,71,73,79,83,89,97の10個の素数については

53*2>100より

Sの要素xが53以上の素因数をもつとき、xは素数

よって53以上の素数は別個にカウントすれば良い。

25-10 = 15個の素数についてビットDPすれば

2^15*|S| <= 2^15 * 50 = 1638400

で間に合う。