プログラミング備忘録

プログラミングに関することをつらつらと。

AtCoder Regular Contest 061 + AtCoder Beginner Contest 045

成績

ARC参加で354位。なかなか緑にはならない。

解法メモ

A問題

台形の面積は S=(a+b)\times h/2

B問題

3人の手札と捨てられたカードの文字を変数に持たせて、実際にシミュレートすればよい。3人で最大300枚しかカードを持っていないのですぐに終わる。

C問題

結局のところ、与えられた数字列をバラバラに分解して、数字と数字の間に順番に+を入れたり入れなかったりしていけばよい。
ビット演算をつかえば効率が良い。最大で10文字しかないので、使うビットも最大9個だから十分。

D問題

C++とかならばmapを使えば一発らしいが、あいにくC言語にそういうのは用意されていない。
黒マスは最大で 10^5 個しかないから、与えられた黒マスに対して、そのマスによって黒マスの数が変化するような3行3列のマスを列挙していけばよい(最大 9\times 10^5)。3行3列のマスが識別できればよいから、どこか1つのマス(例えば左上)を代表として表すことにすればよい。
列挙したマスを上・左からの昇順でソートすると、それぞれの3行3列マスには出現した回数だけ黒マスがあることになる。
列挙したマスを順番に見ていって、1~9個あるマスの個数を数え、最後にその総和 S を計算しておけば、黒マスのない3行3列マスは (H-2)\times(W-2)-S 個あることがわかる。

わかってしまえばあっという間なのにわからなかったのが悔しい。

E問題

二部グラフを使えばよいらしい。まだわかっていない。

F問題

捨てられたカードを順番に見ていくことにする。たとえば、abcbacaa とカードが捨てられてゲームが終わったとする。

  • ゲームが終わったときにAのターンだから、Aが勝ったことになる。すなわち、Aが勝つためには最後に捨てられたカードが a でなくてはならない。
  • Aのターンでゲームが始まって、Aのターンでゲームが終わったから、Aは最初カードを4枚 (abca) 持っていたことになる。これは、a が書かれた捨てられたカードの枚数と一致する。
  • Bは先頭から ca 、Cは先頭から ba というカードを順に持っていたことがわかる。これも、b, c が書かれた捨てられたカードの枚数に一致する。

以上のことから捨てられるカードの順列は、最後に a が来るような、N 個の a、M 個以下の b、K 個以下の c の並べ方の総数に帰着する。

いま、i ターン目にAが最後に a のカードを捨てて勝ったとする。
並べる b の個数を X、c の個数を Y とおくと、Bの持つ M-X 枚の手札とCの持つ K-Y 枚の手札はどの文字が書いてあってもよい。すなわち、カードの組み合わせは 3^{M+K-X-Y} 通り。
i = N+X+Y だから、3^{M+K-X-Y} = 3^{N+M+K-i}
i ターン目までに捨てられた i-1 枚のカードのうち、a のカードが N-1 枚、残るi-N 枚が b, c のカードである。この組み合わせの数は、
\displaystyle {}_{i-1}C_{N-1}\;{}_{i-N}C_X = \frac{(i-1)!}{(N-1)!(i-N)!}\frac{(i-N)!}{(i-N-X)!X!} = \frac{(i-1)!}{(N-1)!(i-N-X)!X!}
通り。
したがって、求める数は
\displaystyle \sum_{i=N}^{N+M+K} \sum_{X=0}^{\min(M,i-N)} 3^{N+M+K-i} {}_{i-1}C_{N-1}\;{}_{i-N}C_X
10^9+7 で割ったあまり。

二項係数を求めるところは、フェルマーの小定理を使って、i!(i!)^{-1} (0 \leq i \leq 900000)を事前に求めておく。

部分点解法

愚直に二項係数の総和を求めると時間を食うので、部分点しかとれない。

満点解法

二項係数 {}_nC_r (0 \leq r \leq n) の総和は 2^n である(二項定理)。このことを利用して、二項係数の和を一気に求める。

f:id:k-ashiya:20160919175048p:plain

図の破線で囲まれた部分の二項係数の和を求めてみよう。

  • 1段目の和は1。
  • 2段目の和は、1段目の1が2回現れるので1段目の2倍で2。
  • 3段目の和は、2段目の2つの1がそれぞれ2回ずつ現れるので、2段目の2倍で4。
  • 4段目の和は、3段目の3つの数がそれぞれ2回ずつ現れるので、3段目の2倍で8。
  • 5段目の和は、4段目の左端を除く3つの数がそれぞれ2回ずつ現れ、左端のみ1回現れるので、4段目の2倍から4段目の左端を引いて15。
  • 6段目の和は、5段目の両端を除く2つの数がそれぞれ2回ずつ現れ、両端はそれぞれ1回現れるので、5段目の2倍から5段目の両端を引いて25。
  • 7段目の和は、6段目の中央の数が2回ずつ現れ、両端はそれぞれ1回現れるので、6段目の2倍から6段目の両端を引いて35。
  • 8段目の和は、7段目の2つの数がそれぞれ1回ずつ現れるので、7段目の2倍から7段目の両端を引いて35。

以上のことから、

  • 1段目の和は1。
  • n\;(n \geq 2) 段目の和は、n-1 段目の和の2倍から、追加しない側の n-1 段目の端の数を引いたもの。

というようにして高速に求めることができる。

キーワード

C問題

ビット演算

F問題

フェルマーの小定理、二項定理、二分累乗法