Processing math: 100%

第二回全国統一プログラミング王決定戦本戦 A,B問題メモ

第二回全国統一プログラミング王決定戦本戦

むずかった。前半数問は、大雑把なオーダー記法による計算量見積もりだけでなく、それをどこまで削減できるかの感覚が問われているように感じた。

A - Count Triplets

問題

  • 長さ N の数列 A1,A2,...,AN がある
  • Ai<Aj>Ak となっている整数の組 (1i<j<kN) の個数を求めよ
  • 3N5000
  • 1Ai109

解法

j を決め打つ。選べる ik の個数は 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つの空でない文字列に分割し、元の並び順に S1S6 とする
  • 順に並んだ6つの文字列が「NIKKEI型である」とは、S2=S6 かつ S3=S4 であることをいう
  • S をNIKKEI型に分割する方法の個数を求めよ
  • 6|S|500

解法

いろいろな解き方があるっぽいが、問題文より条件を1つ1つプログラムに落とし込んでいく。

まず S2=S6 のため、S6 の末尾文字が S2 の末尾文字としてそれより4文字以上前に含まれていないといけないので、それを全探索で決め打つ。

これにより、文字を S1S2S3S6 に分割する箇所が固定される。

aabbbbccbb
↓
aabbb bccbb
aabb bbccbb
aab bbbccbb

aabbbbccb b は不可。S2とS6の間に3文字以上なく、S3,S4,S5を取れる場所が無い

次に S6 の長さを決め打ち、S2=S6 となるか確認する。この時、S1 が取れるよう左側に1文字、S3S5 が取れるよう右側に3文字以上残るところまで探索する。

これにより、S1/S2/S3S5/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) だが、S6S3 で一方の長さが長くなれば一方が短くなるし、文字列比較という単純な操作なので間に合う。

前もって各長さでハッシュ値を計算しておくと 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」のサイズを求めよ

解法

FIXME

1
 

programming_algorithm/contest_history/atcoder/2019/1215_nikkei2019_2_final.txt · 最終更新: 2019/12/17 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0