Boot camp for Beginners hard 002【B – Kleene Inversion】をpython解説

Boot camp for Beginners hard

Atcoder Problems という競プロを勉強する上でのコンテンツがあります。URL

今回は、hard100問の内、2問目を解説していきたいと思います。

問題名 【B – Kleene Inversion】

URL

入力  N,K,A

問題
長さNの数列AがK回繰り返される数列Bを作るその中で、転倒数(左の数よりも右の数が小さい)は何個あるかという問題です。

考察

転倒数が起きる条件を場合分けで考える

  • パターン1 同じブロック内で転倒数が起きる場合
    ex) A = [2,4,3]  4と3のように、与えられたAというブロック内で転倒数がある場合
  • パターン2 ブロックをまたいで転倒数が起きる場合
    ex) A = [1,2] K = 2  B = [1,2, 1,2] のようにブロックをまたいで転倒数がある場合

パターン1 同じブロック内で起きる場合

A = [2,4,3]の場合、 4と3のように、与えられたAというブロック内で転倒数がある場合

Aというブロック内での転倒数の数を数えます。1ブロック(A)は最大で2000なので、右の数と左の数それぞれに対して全探索可能です(2000*1999/2)。

p = 0 # パターン1で得られる転倒数
for i in range(N-1):
    for j in range(i+1,N):
        if A[i] > A[j]:
            p += 1

pattern1 = p*k

ここで、AというブロックはK個あるので、同じブロック内での転倒数の数はp*K

パターン2ブロックをまたいで起きる場合

K個のブロックから2つのブロックを選び、選ばれたブロック間での転倒数の数を数えます。
こちらに関しても、選ばれたブロックそれぞれに対して、全探索が可能です(2000*2000/2)

q = 0
for i in range(N):
    for j in range(N):
        if A[i] > A[j]:
            q += 1

pattern2 = q*(K-1)*K//2 # (K-1)*K//2 はブロックの選び方

ここで、2つのブロックの選び方はK(K-1)/2通りあるので、ブロック間での転倒数の数は、qK*(K-1)//2

コード

なので最終的なコード

import sys
def LI(): return list(map(int,sys.stdin.readline().rstrip().split()))
def II(): return int(sys.stdin.readline())
def MI(): return map(int,sys.stdin.readline().rstrip().split())
def S(): return sys.stdin.readline().rstrip()
import math
readline = sys.stdin.readline
sys.setrecursionlimit(10 ** 6)

N,K =MI()
A = LI()
mod = 10**9+ 7

p = 0
q = 0
for i in range(N-1):
    for j in range(i+1,N):
        if A[i] > A[j]:
            p += 1

q = 0
for i in range(N):
    for j in range(N):
        if A[i] > A[j]:
            q += 1

ans = (p*K + q*(K-1)*K//2) % mod
print(ans)

まとめ

今回のように一見難しそうな問題も、要素要素に分解して考えてみると意外と解けたりするのが、競技プログラミングの面白いところ。

前の問題【D – Gathering Children】
次の問題【A – Range Flip Find Route】

コメント

タイトルとURLをコピーしました