JSC2019 Qual B - Kleene Inversion
備忘録
問題
回答
import sys import os import math import bisect import itertools import collections import heapq import queue import array import time import numpy as np from numpy import linalg as LA # 時々使う # import numpy as np # from decimal import Decimal, ROUND_HALF_UP # from scipy.sparse.csgraph import csgraph_from_dense, floyd_warshall # from collections import defaultdict, deque # 再帰の制限設定 sys.setrecursionlimit(10000000) def ii(): return int(sys.stdin.buffer.readline().rstrip()) def il(): return list(map(int, sys.stdin.buffer.readline().split())) def fl(): return list(map(float, sys.stdin.buffer.readline().split())) def iln(n): return [int(sys.stdin.buffer.readline().rstrip()) for _ in range(n)] def iss(): return sys.stdin.buffer.readline().decode().rstrip() def sl(): return list(map(str, sys.stdin.buffer.readline().decode().split())) def isn(n): return [sys.stdin.buffer.readline().decode().rstrip() for _ in range(n)] def lcm(x, y): return (x * y) // math.gcd(x, y) MOD = 10 ** 9 + 7 # MOD = 998244353 INF = float('inf') def main(): if os.getenv("LOCAL"): sys.stdin = open("input.txt", "r") N, K = il() A = il() ret = 0 for i in range(N): overall, right = 0, 0 for j in range(i+1, N): # Aの内部に存在する転倒数 # (A[i]より右に存在する転倒数) if A[i] > A[j]: right += 1 for j in range(N): # A[i]より小さい値 # (Aの内部に存在するA[i]の転倒数) if A[i] > A[j]: overall += 1 # A[i]より右に存在する転倒数は K回出現する ret += right * K ret %= MOD # A[i]より小さい値は全体でK * (K - 1) // 2回出現する # =>等差数列の和の公式(n(a + l)//2)より # 初項0, 末項overall * (K-1), 項数Kから求める ret += overall * (K - 1) * K // 2 ret %= MOD print(ret) if __name__ == '__main__': main()
考え方
求める転倒数は2種類存在します。
一つ目はA
の内部に存在する転倒数です。
あるi, j(0 <= i <= j <= N-1)
を対象としたとき、A[i] > A[j]
となる転倒数です。
この転倒数はK
回出現します。
2つ目はA
が複数個結合されたとき(B
となったとき)、初めて出現する転倒数です。
A
が複数個結合された場合に出現するため、A
に存在する値のうち、ある値A[i](0 <= i <= N-1)
よりも小さい値が全て転倒数の対象となります。
この転倒数の出現回数は等差数列の和から求めることが出来ます。
(こちらの解説が分かりやすいです B - Kleene Inversion - 個人的な競プロメモ)