−目次
第二回全国統一プログラミング王決定戦本戦 A,B問題メモ
むずかった。前半数問は、大雑把なオーダー記法による計算量見積もりだけでなく、それをどこまで削減できるかの感覚が問われているように感じた。
A - Count Triplets
問題
- 長さ N の数列 A1,A2,...,AN がある
- Ai<Aj>Ak となっている整数の組 (1≤i<j<k≤N) の個数を求めよ
- 3≤N≤5000
- 1≤Ai≤109
解法
j を決め打つ。選べる i と k の個数は j よりそれぞれ前と後ろにある Aj より小さい数の個数である。
そしてこの2つは独立に選んでよいので、3つの組としては単純に掛け合わせた分だけ存在する。
各 j につき前と後ろにある自分より小さい数を掛け合わせた数を求め、それらを合計したものが答え。
転倒数を数える要領でBinary Indexed Tree等を使ってもいいし(ただし座標圧縮が必要)、N が小さいので各 j の両側を毎回探索してもいい。
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 |
class Bit: def __init__( self , n): self .size = n self .tree = [ 0 ] * (n + 1 ) def sum ( self , i): s = 0 while i > 0 : s + = self .tree[i] i - = i & - i return s def add( self , i, x): while i < = self .size: self .tree[i] + = x i + = i & - i n = int ( input ()) aaa = list ( map ( int , input ().split())) bbb = sorted ( set (aaa)) ccc = {b: c for c, b in enumerate (bbb)} m = len (bbb) bit_f = Bit(m) bit_b = Bit(m) smaller_f = [] smaller_b = [] for a in aaa: c = ccc[a] bit_f.add(c + 1 , 1 ) smaller_f.append(bit_f. sum (c)) for a in reversed (aaa): c = ccc[a] bit_b.add(c + 1 , 1 ) smaller_b.append(bit_b. sum (c)) smaller_b.reverse() ans = 0 for i in range ( 1 , n - 1 ): ans + = smaller_f[i] * smaller_b[i] print (ans) |
B - NIKKEI String
問題
- 文字列 S を6つの空でない文字列に分割し、元の並び順に S1~S6 とする
- 順に並んだ6つの文字列が「NIKKEI型である」とは、S2=S6 かつ S3=S4 であることをいう
- S をNIKKEI型に分割する方法の個数を求めよ
- 6≤|S|≤500
解法
いろいろな解き方があるっぽいが、問題文より条件を1つ1つプログラムに落とし込んでいく。
まず S2=S6 のため、S6 の末尾文字が S2 の末尾文字としてそれより4文字以上前に含まれていないといけないので、それを全探索で決め打つ。
これにより、文字を S1~S2 と S3~S6 に分割する箇所が固定される。
aabbbbccbb ↓ aabbb bccbb aabb bbccbb aab bbbccbb aabbbbccb b は不可。S2とS6の間に3文字以上なく、S3,S4,S5を取れる場所が無い
次に S6 の長さを決め打ち、S2=S6 となるか確認する。この時、S1 が取れるよう左側に1文字、S3~S5 が取れるよう右側に3文字以上残るところまで探索する。
これにより、S1/S2/S3~S5/S6 の分割箇所が固定される。
aabb bbccbb ↓ aab b bbccb b aa bb bbcc bb a abb bbc cbb までいくとS2とS6が異なり、これ以上伸ばしても同じになることは無いため、終了
最後に S3 の長さを決め打つ。それに続けて S4 を同じ文字数だけとり、同じになるか確認する。S5 が1文字以上取れるよう注意する。
aa bb bbcc bb ↓ aa bb b b cc bb
これでNIKKEI型の条件を列挙できたので、これらを全て満たすものの個数が答え。 計算量は O(N4) だが、S6 と S3 で一方の長さが長くなれば一方が短くなるし、文字列比較という単純な操作なので間に合う。
前もって各長さでハッシュ値を計算しておくと O(N3) になる。
文字列問題は添字の管理で混乱しがち。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
s = input () n = len (s) ans = 0 s6t = s[ - 1 ] for s2t_pos in range (n - 5 , 0 , - 1 ): if s[s2t_pos] ! = s6t: continue # print(s2t_pos, n-s2t_pos-3, (n - s2t_pos - 3) // 2 + 1) for s6len in range ( 1 , min (n - s2t_pos - 4 , s2t_pos) + 1 ): s2h_pos = s2t_pos - s6len + 1 s6h_pos = n - s6len if s[s2h_pos:s2t_pos + 1 ] ! = s[s6h_pos:n]: break for s3len in range ( 1 , (n - s2t_pos - s6len - 2 ) / / 2 + 1 ): s3h_pos = s2t_pos + 1 s3t_pos = s3h_pos + s3len - 1 s4h_pos = s3t_pos + 1 s4t_pos = s4h_pos + s3len - 1 if s[s3h_pos:s3t_pos + 1 ] ! = s[s4h_pos:s4t_pos + 1 ]: continue ans + = 1 # print('', ans) print (ans) |
C - Largest N
問題
- H×W の盤面があり、K マスは白マス、残りは黒マス
- i 個目の白マスの位置は (ai,bi) で与えられる
- 黒マスだけを拾って盤面から取れる最大の「N」のサイズを求めよ
解法
1 |
|