ARC 086
C - Not so Diverse
問題
- N 個のボール、各ボールには数字 ai が書かれている
- いくつかの数字を書き換えることで、数字の種類を K 種類以下にする
- 書き換える数を最小化
解法
逆に書き換えない数字を最大化することを考える。
各数字毎に、書かれたボールの数を数えて、多い方から K 番目までの数が書かれたボールは書き換えずに残しておくと、最大となる。
10 2 1 1 1 2 2 3 4 4 4 5 数字毎にボールの数を数える {1:3, 2:2, 3:1, 4:3, 5:1} ボール数が多い順にソート [(1,3), (4,3), (2,2), (3,1), (5,1)] 多い方からK=2番目までの数字は 1 と 4 それらが書かれたボールの数は計 6 個で、これをこのまま書き換えないのが最善 残りの4個を書き換えればよい
1 2 3 4 5 6 7 8 9 10 11 |
from collections import Counter from operator import itemgetter n, k = map ( int , input ().split()) a = Counter( map ( int , input ().split())) sa = sorted (a.items(), key = itemgetter( 1 ), reverse = True ) if len (sa) < = k: print ( 0 ) else : print ( sum (c for m, c in sa[k:])) |
D - Non-decreasing
問題
- N 要素の数列 a=a1,a2,...,aN
- 「任意の数 x,y(1≤x,y≤N) を選んで、ay に ax を加算」という操作を 2N 回まで行える
- 操作の結果、a を単調増加列 (a1≤a2≤...≤aN) としたい
- 操作手順の一例を示せ
解法
a が全て非負なら、累積和を取ると条件を満たす(N−1 回の操作)
a が全て非正なら、後ろから累積和を取ると条件を満たす(N−1 回の操作)
a が正負混在なら、最大値(=正)と最小値(=負)、どちらの絶対値が大きいか調べ、最大値が大きければ負の要素に最大値を、最小値が大きければ正の数に最小値を足すと、全て非負または非正にできる(最大N−1 回の操作)
反省
考えれば当たり前なんだけど、なんか累積和がパッと思いつかなかったなあ……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
from itertools import starmap def solve(n, a): max_a, min_a = float ( '-inf' ), float ( 'inf' ) max_i, min_i = 0 , 0 for i, b in enumerate (a): if b > max_a: max_i, max_a = i + 1 , b if b < min_a: min_i, min_a = i + 1 , b if max_a > = 0 and min_a > = 0 : return [(i, i + 1 ) for i in range ( 1 , n)] if max_a < = 0 and min_a < = 0 : return [(i, i - 1 ) for i in range (n, 1 , - 1 )] buf = [] if max_a > = - min_a: for i, b in enumerate (a): if b < 0 : buf.append((max_i, i + 1 )) buf.extend((i, i + 1 ) for i in range ( 1 , n)) else : for i, b in enumerate (a): if b > 0 : buf.append((min_i, i + 1 )) buf.extend((i, i - 1 ) for i in range (n, 1 , - 1 )) return buf n = int ( input ()) a = list ( map ( int , input ().split())) buf = solve(n, a) print ( len (buf)) print (' '.join(starmap(' {} {}\n'. format , buf))) |
E - Smuggling Marbles
問題
上記リンク参照
解法
根0からの深さが等しいものしか干渉しないので、まずはノードを深さごとに仕分けする。
(※干渉=初期状態でノード n1 と n2 に置かれたボールが、どこかのタイミングで1つのノードに同時に存在し取り除かれる可能性がある場合、n1 と n2 は干渉するということにする)
また、任意の部分木を考えると、その部分木の中では、外のボールが干渉することは無い。小さな部分木から解いていって、親に遷移するときに兄弟とマージしていく動的計画法が考えられる。
dp[n][i][k] = i 回の操作後に、ノード n に上ってくるボールが k 個になる場合の数(同階層内のみでの場合の数、他の階層を考慮しない)
ただし i = 0は初期状態を示す。また k = 0,1,2以上 の3通りのみ保持。場合の数において他の階層を考慮しない理由は、最後にまとめて計算できるので、途中で逐一やる必要が無いから。
このdpを、階層が深い位置にある n から埋めていく。
最終的に ∑i=0dp[0][i][1] が答え…ではなく、これは違う階層に属するノードの初期状態の組み合わせを考慮できてないので、その組み合わせの数をかける必要がある。
つまり、第 i 階層に属さないノードの数をmiとすると、∑i=0(dp[0][i][1]×2mi) が答えとなる。
0 /\ 1 4 /\ \ 2 3 5
dpのやり方: 子が無いノードについて
まず、子が無いノードは、自身に初期状態でボールが置かれてるか置かれてないかしかない。i = 0 のみであり、その時の場合の数[0個,1個,2個以上] = [1,1,0] となる。
よって、ノード 2,3,5について、dp[2] = dp[3] = dp[5] = [[1,1,0]] となる。
dpのやり方: 子があるノードについて
まずi=0は、子が無いノードと同様[1,1,0]である。
i=1以降、子からのボールを統合することになる。2つずつマージできる。
ノード v の子を c1,c2,... とし
dp[c1][i]=[a0,a1,a2]dp[c2][i]=[b0,b1,b2]
であったとき、c1 と c2 の統合結果は、以下になる。(※処理1)
便宜的に、統合の途中結果を dp[ctmp] と表す。(実際にdpへの登録を行うわけでは無く、あくまで dp[ctmp] のデータ構造や意味が dp[c1] などと同じであることを示す)
dp[ctmp][i][0]=a0b0dp[ctmp][i][1]=a1b0+a0b1dp[ctmp][i][2]=a2(b0+b1+b2)+a1(b1+b2)+a0b2
数式で書くとわかりにくいが、要は、以下のような場合の数の計算をしているだけ
- (統合結果が1) = (c1から1 ∩ c2から0) ∪ (c1から0 ∩ c2から1)
- (統合結果が1の場合の数) = (c1が1の場合の数 × c2が0の場合の数) + (c1が0の場合の数 × c2が1の場合の数)
2つの子の深さが異なるとき、例えば c2 より c1 の方が部分木が深く、dp[c1][i] に対応する dp[c2][i] が無い、という時は、それ以降の i については dp[c1][i] のままでよい
順次 ctmp にマージしていき、全ての子について統合が完了すると、次は「同じノードに2個以上同時にボールがあると取り除く」という操作に基づき、各 i につき、以下を行う。(※処理2)
dp[ctmp][i][0]+=dp[ctmp][i][2]dp[ctmp][i][2]=0
この処理の後、dp[ctmp] の先頭に[1,1,0]を付け加えたものが、dp[v] となる。
ただ、これをそのまま実装すると、部分点は取れるが全ては時間的に間に合わないので、不要な処理をカットする。
子の数が1つの時
マージする必要は無く、dp[v] は dp[c] の先頭に[1,1,0]を加えればよい
子の数が2つ以上の時
最も深い子の深さ(dp[c]の長さ)を d1, 2番目に深い子の深さを d2 とすると、上記の※処理2(統合後、2個以上の場合の数を0個に移す処理)は、d2≤i<d1の範囲については、行う必要は無い。
dpに登録するときは必ず※処理2によって「2個以上」の場合の数を 0 にしているので、更新が無かった部分については繰り返し行う必要が無い。
これらは、特に階層の深い入力ケースに対する枝刈りとして有効。
データ構造について
子を統合するとき、新しく配列を作るのでは無く「dp[c]の中で最長のもの」に対して統合するようにすると、余計なメモリを消費せずに済み、処理時間も速くなる。
その際、[1,1,0]を先頭に付け加える処理を頻繁に行うので、各 dp[n] は双方向リストで持つとよい。
仮に全てのdp[n]を保存していた場合、N≤2×105 で、全てが直列に繋がっているような意地悪なケースだと、dp[n]のサイズは下からの階層に比例するので上は N に近くなり、それが N 個あり、それぞれが3つの状態を記録するので、3N(N−1)/2=6×1010 個のintをもつことになり、MLEの可能性もある。
処理の終わった階層の情報が正しくなくなるが、問題を解くには使わないので問題ないことにする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
from collections import deque def get_pow(): cache = {} def func(x): if x not in cache: cache[x] = pow ( 2 , x, mod) return cache[x] return func mod = 1000000007 n = int ( input ()) parents = list ( map ( int , input ().split())) children = [ set () for _ in range (n + 1 )] for c, p in enumerate (parents): children[p].add(c + 1 ) levels = [{ 0 }] while True : level = set () for p in levels[ - 1 ]: level.update(children[p]) if not level: break levels.append(level) levels.reverse() level_node_count = [] balls = [ None ] * (n + 1 ) for i, level in enumerate (levels): level_node_count.append( len (level)) for node in level: cn = children[node] if cn: if len (cn) = = 1 : bs = balls[ next ( iter (cn))] bs.appendleft([ 1 , 1 , 0 ]) balls[node] = bs continue balls_from_children = [balls[c] for c in children[node]] balls_from_children.sort(key = len ) bs1 = balls_from_children[ 0 ] for bs2 in balls_from_children[ 1 :]: for (b10, b11, b12), b2 in zip (bs1, bs2): b2[ 2 ] = ((b11 + b12) * b2[ 1 ] + b12 * b2[ 0 ]) % mod b2[ 1 ] = (b10 * b2[ 1 ] + b11 * b2[ 0 ]) % mod b2[ 0 ] = b2[ 0 ] * b10 % mod bs1 = bs2 lim = len (balls_from_children[ - 2 ]) for i, b in enumerate (bs1): if i > = lim: break b[ 0 ] = (b[ 0 ] + b[ 2 ]) % mod b[ 2 ] = 0 bs1.appendleft([ 1 , 1 , 0 ]) balls[node] = bs1 else : balls[node] = deque([[ 1 , 1 , 0 ]]) level_node_count.reverse() pow2 = get_pow() print ( sum (b[ 1 ] * pow2(n - l + 1 ) % mod for l, b in zip (level_node_count, balls[ 0 ])) % mod) |