Loading [MathJax]/jax/output/CommonHTML/jax.js

鹿島建設プログラミングコンテスト2020(AtCoder Regular Contest 110)B,C,D,E問題メモ

B - Many 110

問題

  • '110' を 1010 回繰り返した文字列を S とする
  • 文字列 T が与えられるので、TS の中に連続する部分文字列として何回登場するか答えよ
  • 1|T|2×105

解法

例外処理や、答えが1ずれたりしないようにする注意力が必要。

S を実際に作るのはメモリ的に無理だが、|T| よりちょっと長い'110'の繰り返しを作れば、S に登場するかどうかをプログラム標準の文字列検索機能で調べるには十分。登場しなければ答えは0。

登場するなら3つごとに出現するので、T がいくつの'110'に渡って存在するか調べる。
T の先頭文字を調べるなどする。

  • '110'から始まるなら、|T|3 回の'110'が必要
  • '101'から始まるなら、|T|+13 回の'110'が必要
  • '011'から始まるなら、|T|+23 回の'110'が必要

となる。1010110+1 回が答え。

ただし、|T|<3 の時は上手くいかない。個数も少ないし全パターン場合分けする。

Python3

C - Exoswap

問題

  • 1,2,3,...,N の順列 P1,P2,...,PN が与えられる
  • 以下の操作を行って、P を昇順にできるか判定し、できる場合は操作順の一例を示せ
  • 操作
    • 以下の全てを好きな順でそれぞれちょうど1回ずつ行う
      • P1P2 をスワップ
      • P2P3 をスワップ
      • PN1PN をスワップ
  • 2N2×105

解法

i     1  2  3  4  5
Pi    2  5  1  3  4
操作   ① ② ③ ④

端っこの数から考える。最大の数 P2=5は、操作②~④をこの順に1回ずつ使う以外に右端に持っていく方法はない。

すると、もう操作②~④は使えなくなる。 もともと5のあった位置 i=2 はまだ操作①によって入れ替わる可能性があるが、P3P5 はもう動かせない。

i     1  2  3  4  5
Pi    2  1  3  4  5
操作   ① 済 済 済
            ~~~~~~~←FIXED

よって、この時点で i=3,4 について i=Pi になっていないと、不可能となる。

なっている場合は、次に未確定で大きい数字、つまり i=2 に持ってくるべき数字、2について同じことをする。

ただし、この方針だと実装によっては「操作を使わなくても完成できてしまう」パターンで、不可能であることが見つけられない。

i     1  2  3  4  5
Pi    1  2  5  3  4
操作   ① ② ③ ④
↓
Pi    1  2  3  4  5   既に完成している
操作   ① ② 済 済    ①②を使ったら逆に昇順達成が不可能となる

次に未確定で大きい数字を移動させようとしたら既に i=Pi だった、という場合に不可能となるので、それを調べる。
または、最初に転倒数を調べて、N1 個ちょうどであることを確認してもよい。

Python3

D - Binomial Coefficient is Fun

問題

  • 長さ N の非負整数列 A1,A2,...,AN が与えられる
  • 同じく長さ N で、総和が M 以下の非負整数列 B1,B2,...,BN を考える
  • 考えられる全ての B につき Ni=1(BiAi) を求め、その総和を mod109+7 で求めよ
    • Bi<Ai のとき、(BiAi)=0
  • 1N2000
  • 1M109
  • 0Ai2000

解法

天啓のような言い換えか、二項係数に慣れていないとちょっと難しい式変形が必要。小さいサンプルからの推測が現実的か。

まず、Ai>Bii が1つでもあると全体が0になるため、答えが正になるには全て AiBi である必要がある。

なので、Ai の総和を S として、MS でないと、0。以降、Bi を増やせる余地 T=MS0 とする。

ここで、まずは Bi=Ai として、そこからそれぞれ k 増やしたときに (Ai+kAi) がいくつになるかの表を作ると、

k\A  1  2  3
------------
0    1  1  1
1    2  3  4
2    3  6 10
3    4 10 20
....

こんな感じになる。

で、Aiki 増やしたとして k1+k2+...+kNM ならいいので、これは形式的冪級数の考え方でできそうな気になる。

  • f1=1x0+2x1+3x2+4x3+...
  • f2=1x0+3x1+6x2+10x3+...
  • f3=1x0+4x1+10x2+20x3+...
  • →答えは、f1f2f3 の、x の指数が T 以下の項の係数の和

ただ、k の上限を考えると M=109 のオーダーなので、実際に表を全部埋めて、FFTでたたみ込んで、とかそういう解法では無さそう。

二項係数は競プロの解法でよく天才的な式変形をしていることがあるので、今回も単純な式に変形できるのだろう、というのは推測できる。
問題はそれをどうやって見つけるか。

実際に A を適当に決めて、「各項に加えた ki の和がちょうど 0,1,2,... である時の (Ai+kiAi)」を手計算すると、

k\A  1  2  3
------------
0    1  1  1      1   1x1x1
1    2  3  4      9   2x1x1, 1x3x1, 1x1x4
2    3  6 10     45   3x1x1, 1x6x1, 1x1x10, 2x3x1, 2x1x4, 1x3x4
3    4 10 20    165   略

なんか出てくる。1,9,45,165 - OEIS

他にもサンプルを少し変えて試してみると、これは (S+N1+kS+N1) であると推測できる。

ならば、答えは Tk=0(S+N1+kS+N1) なので、これは (T+S+NS+N)=(M+NS+N) で一発で求められることが分かる。

ちゃんとした考察

一気に考えようとせず、まず2つの二項係数を合成することを考える。

(A1+k1A1)(A2+k2A2)k1+k2=k となるものは、 km=0(A1+mA1)(A2+(km)A2) と表せる。

これ、(どうやって求めたらいいのか分からんが)WolframAlphaさんなどに投げると (A1+A2+k+1A1+A2+1) となる。

つまり、合成前と同じ形式の二項係数で表せるので、この結果を A3 とマージ、さらにその結果を A4 とマージ……としていくと、 最終的な式は (A1+A2+...+AN+k+N1A1+A2+...+AN+N1)=(S+N1+kS+N1) となる。

これが「k1+k2+...+kN=k の時の (Ai+kiAi) の値」を表す。

あとはこれを k=0T について合計する部分は上記と同じ。

答えの式の意味するところ

公式解説にもあるが、何故これで行けるのかをイメージしやすく考えると、M+N 個のボールを並べて、

N=3  M=9
A = {1, 2, 3}

○○○○○○○○○○○○

左から i 番目の区切りの長さが Ai 未満にならないように N 個のボールを選んで黒く塗り、各区間の長さを Bi(一番右は使わないあまり)とすると、

  ○○●○○●○○○○●○
B   2     2       4

(BiAi) は、その意味からしても「各区切りから Ai 個のボールを選ぶ選び方」と一致する。

ここで、「区切り」と「各区切りから Ai 個」を区別せず、N+S 個を一度に選んでみると、

●○●●●●○●●●●○

黒いボールの内、
左からA1=1個目までは A1 で選ばれたボール
A1+1 個目は区切りのボール
その次からA2=2個目までは A2 で選ばれたボール
A2+1 個目は区切りのボール
...

●○|●●|○●●●|○

というように、Bi の決め方とその中での選び方を一意に再現でき、1対1対応することがわかる。

この一度に選ぶ選び方が、今回の式 (M+NS+N) となる。

経路数えあげに例える方法もあるらしい。

Python3

E - Shorten ABC

問題

  • 長さ N の'A','B','C'からなる文字列 S が与えられる
  • 以下の操作を好きなだけ(0回でもよい)繰り返して作ることのできる文字列が何通りあるか、mod109+7 で求めよ
  • 操作
    • 異なる文字が隣り合う部分を選び、その2文字を'A','B','C'のうちそのどちらでもない1文字に置き換える
  • 1N106

操作回数
0           ABAAC
1      CAAC  ACAC  ABAB
2      ABC  ACB  BAC  CAB
3        AA  BB  CC

S=ABAAC のとき、11通り

解法

若干の競プロテクニックの知識と、DP。

競プロテクニック

「ABCからなる文字列の異なる2文字を、そのどちらでもない1文字に置き換える」という操作をしたときに、不変量(操作前と操作後で変わらない指標)がある。
それは、A=1,B=2,C=3 としたときの、総XORである。

AB → 01 XOR 10 = 11 = C
AC → 01 XOR 11 = 10 = B
BC → 10 XOR 11 = 01 = A

これより、操作を行う箇所(縮約する文字の組み合わせ)が同じなら、その順番は影響しないことがわかる。

DP

操作順が関係ないので、「S1i の部分文字列から作れる文字列は、S1i1 から作れる文字列の末尾に Si を追加したものか、追加した上で末尾に対して1回操作を行ったもの」といえそう。

以下のようなDPを定義すると更新できる。

  • DP[i][k:A,B,C]=S1i の部分文字列に操作して作れる文字列で、末尾が k であるものの個数

Si=A だったとすると、以下のようになる。

  • 追加したままの場合
    • DP[i][A]=DP[i1][A]+DP[i1][B]+DP[i1][C]
  • 追加して操作した場合
    • DP[i][B]=DP[i1][C]
    • DP[i][C]=DP[i1][B]
ABAAから   ABAAC から
作れる  → 作れる

ABAA       ABAAC  ABAB
ACA        ACAC   ACB
CAA        CAAC   CAB
AB         ABC    AA
BA         BAC    BB
C          CC     ↑
           ↑     追加して1回操作
         そのまま追加

末尾から遡って2回以上操作を行うことは考えなくてよい。

ABCA: S[1~i-1] から作れる文字の1つ
C   : S[i]

ABCAC    : 追加したまま
ABCB     : 追加して1回操作

ABA      : 追加して2回操作したもの

S[1~i-1] から作れる文字の中には、
ABB(ABCAの末尾に先に操作したもの)も同様に数えられているはず。
ABA は、ABBに対して C を追加して1回操作したものとして数え上げられる。

ただし、同じ文字に対しては操作を行えないことに起因して、例外的な挙動をすることがある。

例外1.最初の同じ文字の連続

S='AAAAB' の場合を考えると、'AAAA'までを考慮した場合は操作できないので1通りだが、新たに'B'が来たら突然、操作できる候補が増える。

AAAAB
AAAC
AAB
AC
B

DPの更新では末尾に追加した文字はその直前との操作しか考慮しないので、AAB,AC,Bを考慮から漏らしてしまう。

そのため、最初に同じ文字 c1p 文字連続するとすると、 異なる文字 c2 が来た時点で「c2 を末尾とするものが p/2 個」「c3 を末尾とするものが (p1)/2 個」増える。

これはあくまで最初の同じ文字の連続のみで、途中だったら、前の方から操作できるのでちゃんと考慮できている。

CAAAAAAAB
~~  ~~~~~ ←ここを縮約すると BAAB ができる。
            末尾に追加したBは遡って4回操作していることになるが、考慮できてるのか?
            
~~~~  ~~~   同じ文字が連続する箇所は、縮約箇所を2文字ずつずらしても作れる文字列は変わらない。
~~~~~~  ~   「Bとの操作が必要な末尾のAは0個か1個」という状態まで右にずらしたものが既に考慮済。
例外2.直前がXOR=0のとき

(A,B,Cを1,2,3に置き換えて)先頭からの累積XORを取ったら0になる場合、 操作を可能な限り繰り返して最後に残るのは、'AA','BB','CC'のように同じ文字が2つ続くものとなる。ただしどれが可能かは S による。

その場合、例外1.のミニバージョンのようなことがおこる。

AAB
AC
B

AAB,ACは考慮できているが、2回操作を行った'B'は考慮から漏れている。これを加算する。

「もし最後に残る可能性があるのが'AA'のみで、追加するのも'A'だったら操作できないじゃないか」となるが、 最初の同じ文字の連続以外(1回以上の操作を行っている)場合、'AA'の直前は'BCA'や'ABC'などであり、そしたら'BB'か'CC'の少なくとも片方は作れる。


う~ん、とりあえずサンプルは合致したが、例外がこれで全てであるという感覚がいまいちつかめないな。

Python3

programming_algorithm/contest_history/atcoder/2020/1205_arc110.txt · 最終更新: 2020/12/23 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0