JOI2007本戦C 最古の遺跡

この記事で使うアルゴリズム

全探索

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

https://atcoder.jp/contests/joi2007ho/tasks/joi2007ho_c
https://www.ioi-jp.org/joi/2006/2007-ho-prob_and_sol/2007-ho.pdf#page=5
この問題は全探索を用いて解くことができます。

方針

愚直な全探索はO(n^4)となり間に合いません。
そこで2点p1,p2を考え、時計回りに正方形を作ります。この時、残りの2つの点p3,p4は一意に定まります。
つまり、全ての2点の組み合わせp1,p2に対して、p3,p4が存在するかどうかを調べれば良いです。
存在の確認をハッシュテーブルなどでO(1)で実現できれば、問題を計算量O(n^2)で解くことができます。

Pythonの場合、setdictはハッシュテーブルで実装されています。

あらかじめ点の組み合わせをソートしておくことで、2点の順列ではなく2点の組み合わせを考えれば良くなり、2倍高速になります。
三平方の定理を使うことで、正方形の面積は(x1-x2)**2+(y1-y2)**2で表せることがわかります。

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from itertools import combinations
N = int(input())
ls = []
for n in range(N):
x, y = map(int,input().split())
ls.append((x,y))
ls.sort()
st = set(ls)
ans = 0
for p1, p2 in combinations(ls, 2): # 組み合わせでOK
x1, y1 = p1
x2, y2 = p2
if (x2 + y2 - y1, y2 + x1 - x2) in st and (x1 + y2 - y1, y1 + x1 - x2) in st:
ans = max(ans, (x1-x2)**2+(y1-y2)**2)
print (ans)

記事情報

  • 投稿日:2020年5月7日
  • 最終更新日:2020年5月8日

ABC150D Semi Common Multiple

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

https://atcoder.jp/contests/abc150/tasks/abc150_d

方針

基本的には解説PDFの通りです。

  • a_k / 2を考え、それらの最小公倍数を奇数倍したものの数を数えれば良いです。
  • ただし、a_kのそれぞれが2で割れる回数が全て等しくない場合、条件を満たすXが存在しません。

コード

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
from fractions import gcd
from functools import reduce

def lcm(a,b): # lcmは標準ライブラリには存在しない
return a * b // gcd(a,b)
def ls_lcm(A): # リストを引数に取れる
return reduce(lcm ,A)
def ls_gcd(A): # リストを引数に取れる
return reduce (gcd, A)

def count_two(n): # 2で割れる回数を取得
cnt = 0
while(n & 1 == 0): # 最下位ビットが0かどうか
cnt += 1
n = n >> 1 # 右ビットシフト
return cnt

N, M = map(int, input().split())
A = list(map(int, input().split()))

if len(set([count_two(a) for a in A])) != 1:
print (0)
exit()
A = [a//2 for a in A] # a_k/2 を考える
lc = ls_lcm(A)
print ((M + lc) // (2*lc)) # 最小公倍数を奇数倍したものが条件を満たす

記事情報

  • 投稿日:2020年5月6日
  • 最終更新日:2020年5月8日

ABC075C Bridge

この記事で使うデータ構造

Union-Find

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

https://atcoder.jp/contests/abc075/tasks/abc075_c

この問題はUnion-Findと呼ばれるデータ構造で解くことができます。

方針

解説PDFの通り、
この問題のようなグラフの連結判定にはBFSDFSワーシャルフロイド法Union-Findなど様々な方法で解けますが、この記事ではUnion-Findで解きます。

Union-Findとは

互いに素なデータ集合を管理するためのデータ構造です。
Union-Findは以下の二つの機能を持っています。

  • union 二つの集合を併合する
  • find 指定した要素がどの集合に属するかを判定する

データ構造そのものはdisjoint-setと呼び、それに対する操作をUnion-Findと呼ぶ場合もありますが、この記事ではデータ構造をUnion-Findと呼ぶことにします。

コード

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
class UnionFind:
def __init__(self, n):
self.nodes = n
self.parents = [i for i in range(n)]
self.sizes = [1] * n
self.rank = [0] * n

def find(self, i): # どの集合に属しているか(根ノードの番号)
if self.parents[i] == i:
return i
else:
self.parents[i] = self.find(self.parents[i]) # 経路圧縮
return self.parents[i]

def unite(self, i, j): # 二つの集合を併合
pi = self.find(i)
pj = self.find(j)
if pi != pj:
if self.rank[pi] < self.rank[pj]:
self.sizes[pj] += self.sizes[pi]
self.parents[pi] = pj
else:
self.sizes[pi] += self.sizes[pj]
self.parents[pj] = pi
if self.rank[pi] == self.rank[pj]:
self.rank[pi] += 1
def same(self, i, j): # 同じ集合に属するかを判定
return self.find(i)==self.find(j)

def get_parents(self): # 根ノードの一覧を取得
for n in range(self.nodes): # findで経路圧縮する
self.find(n)
return self.parents

adj = []
N, M = map(int,input().split())
for m in range(M):
a,b = map(int,input().split())
adj.append([a-1,b-1])

ans = 0
for i in range(M): # 取り除く辺番号
uf = UnionFind(N)
for j in range(M):
if (i==j): # 辺を追加しない(取り除く)
continue
uf.unite(*adj[j])

if len(set(uf.get_parents()))!=1: # 複数の集合にわかれているか確認
ans += 1
print (ans)

記事情報

  • 投稿日:2020年5月6日
  • 最終更新日:2020年5月8日

ARC067A Factors of Factorial

問題

https://atcoder.jp/contests/arc067/tasks/arc067_a
素因数分解の問題です。試し割り法と呼ばれるアルゴリズムで解くことができます。

アルゴリズムの詳細はこちらをご覧ください。
NTL1A Prime Factorize

方針

約数の個数は、 各素因数で何度割ることができるかがわかれば計算することができます。この問題はNの上限が小さいため、愚直に素因数分解のループを回せば十分です。

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
MOD = 10**9+7
def factorize(N):
n = N
fact = []
for i in range(2,int(N**0.5)+1): # intに変換すること
while(n%i==0):
n //= i # 整数除算(//)を使うこと
fact.append(i)
if n!=1:
fact.append(n)
return fact
N = int(input())
cnt = [0] * (N+1)
for n in range(1,N+1):
fact = factorize(n)
for f in fact:
cnt[f] += 1

ans = 1
for n in range(N+1):
ans *= (cnt[n]+1)
ans %= MOD
print (ans)

関連記事

他にも競技プログラミングの記事を書いています。

記事情報

  • 投稿日:2020年5月6日
  • 最終更新日:2020年5月6日

ABC014C AtColor

この記事で使うアルゴリズム

累積和
いもす法

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

https://atcoder.jp/contests/abc014/tasks/abc014_3

この問題では累積和を応用したいもす法というアルゴリズムで解くことができます。

方針

要素数1000000のカウンタを用意して、アンケート毎に対象の色をカウントアップすると、計算量はO(1000000n)となり間に合いません。そこでいもす法
というテクニックを利用します。

いもす法

いもす法は、配列上の連続する区間にある同じ数を足したい時に有効なテクニックです。累積和の逆のようなテクニックです。例を使って説明します。

まずは累積和のおさらいです。下記のようなリストAを考えます

1
A = [2, 3, 0, 1, 2]

Aの累積和Sは以下のようになりますね。

1
2
A = [2, 3, 0, 1, 2]
S = [2, 5, 5, 6, 8]

さて次に累積和の逆を考えます。

1
S = [0, 0, 1, 1, 1, 0, 0]

このような累積和Sを与えるAはどのようになるでしょうか。

1
2
S = [0, 0, 1, 1, 1, 0, 0]
A = [0, 0, 1, 0, 0,-1, 0]

このようになるはずです。

さて、先ほどのSのような配列が複数(S1~S3)あり、それらを足したい状況を想定しましょう。
ただし、まだ配列の形で保持しておらず、以下のように値が1となっている箇所を示す形の情報だとします。

1
2
3
S1: 2~4
S2: 3~5
S3: 2~5

この時、配列にもし展開すると下記のようになります。

1
2
3
S1 = [0, 0, 1, 1, 1, 0, 0]
S2 = [0, 0, 0, 1, 1, 1, 0]
S3 = [0, 1, 1, 1, 1, 1, 0]

これらの和Sを求めようとすると配列の数をN、各配列の要素数をKとして計算量はO(NK)となります。

一方で、先ほどのように累積和がSとなるようなAを考えます。先ほどの1となっている箇所を示す形の情報を元にするとAの作成コストは、O(N+K)です。

1
A = [0, 0, 2, 1, 0,-1,-2]

この累積和を求めると、

1
2
A = [0, 0, 2, 1, 0,-1,-2]
S = [0, 0, 2, 3, 3, 2, 0]

となります。これでより少ない計算量O(N+K)Sを求めることができました。

コード

今回の問題は、まさにいもす法の典型問題です。以下のようにシンプルに実装できます。

1
2
3
4
5
6
7
8
9
import itertools
N = int(input())
imos = [0] * (10**6 + 2)
for n in range(N):
a, b = map(int,input().split())
imos[a] += 1
imos[b+1] -= 1
csum = list(itertools.accumulate(imos))
print (max(csum))

記事情報

  • 投稿日:2020年5月5日
  • 最終更新日:2020年5月8日

GigaCode2019D 家の建設

この記事で使うアルゴリズム

累積和

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

https://atcoder.jp/contests/gigacode-2019/tasks/gigacode_2019_d

この問題では累積和の計算が求められます。

方針

H方向の座標がh1+1,h1+2,...,h2、W方向の座標がw1+1,w1+2,...,w2である長方形についてマス内の和を求めたいとします。
この時、二次元の累積和Cを考えます。そして、C[h2][w2] - C[h1][w2] - C[h2][w1] + C[h1][w1]を求めれば良いです。

単純にこの解法だとPyPyであっても際どくTLEとなってしまったので、泥臭いですが一部の計算をメモして高速化しました。

コード

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
import itertools
def transpose(mat): # 二次元のリストを転置する関数
return list(zip(*mat))
H, W, K, V = map(int, input().split())
A = [[0] * (W+1)] # 1行目は0にしておくと計算が楽

for h in range(H):
a = list(map(int,input().split()))
a = [0] + a # 1列目は0にしておくと計算が楽
A.append(a)

B = []
for a in A:
csum = list(itertools.accumulate(a))
B.append(csum)
B = transpose(B)

csum_mat = []
for a in B:
csum = list(itertools.accumulate(a))
csum_mat.append(csum)
csum_mat = transpose(csum_mat)

ans = 0
for h1 in range(H):
if ((H-h1) * W < ans):
continue
ch1 = csum_mat[h1] # メモ
for h2 in range(h1,H+1):
if ((h2-h1) * W < ans):
continue
h2h1 = h2 - h1 # メモ
ch2 = csum_mat[h2] # メモ
for w1 in range(W):
if (h2h1 * (W-w1) < ans):
continue
h2w1h1w1 = - ch2[w1] + ch1[w1] # メモ
for w2 in range(w1,W+1):
s = h2h1 * (w2-w1) # 面積
if (s < ans):
continue
cost = ch2[w2] - ch1[w2] + h2w1h1w1
cost += K * s
if (cost <= V):
ans = s
print (ans)

記事情報

  • 投稿日:2020年5月5日
  • 最終更新日:2020年5月8日

JOI2010本選A 旅人

この記事で使うアルゴリズム

累積和

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

https://atcoder.jp/contests/joi2010ho/tasks/joi2010ho_a

この問題では累積和の計算が求められます。

Pythonの場合、itertools.accumulateを用いると良いでしょう。

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import itertools
MOD = 10**5
N, M = map(int, input().split())
D = [0] # 先頭に0を入れておくと、累積和の差分計算の際に楽
for n in range(N-1):
d = int(input())
D.append(d)
C = []
for m in range(M):
c = int(input())
C.append(c)

csum = list(itertools.accumulate(D))
cur = 1
ans = 0
for c in C:
next = cur + c # 移動
ans += abs(csum[next-1]-csum[cur-1]) # 絶対値を足す
cur = next # 次の移動開始地点
ans %= MOD
print (ans)

記事情報

  • 投稿日:2020年5月4日
  • 最終更新日:2020年5月8日

ABC106D AtCoder Express 2

この記事で使うアルゴリズム

累積和

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

https://atcoder.jp/contests/abc106/tasks/abc106_d

この問題では累積和の計算が求められます。

方針

愚直にクエリ毎に全ての列車を調べると、計算量はO(MQ)となり間に合いません。

そこでに二次元空間に展開することで計算量を削減します。具体的には縦軸を始点L、横軸を終点Rとして、 区間LiからRiまで走る列車iを点として表します。全く同一の区間を走る列車もあるので、二次元配列上にカウントするのが良いでしょう。

このようにデータを展開しておくことで、クエリに対して二次元配列上の対応する長方形の中の数の和を求めれば良いことになります。この方法だと、計算量はO(N^2 Q)となり、今回の制約では先ほどとあまり変わりません。高速化が必要です。

区間の和を複数回求める場合、累積和を使うことで高速化できる場合があります。(O(N)O(1)になります。)

累積和とはA = [a_1, a_2,..., a_n]に対してs_i = a_1 + a_2 + ... + a_iのように定義される値です。いかに例を示します。

配列の値 1 4 0 2 3
累積和 1 5 5 7 10

Pythonで累積和を実装する場合、itertools.accumulateを用いると良いでしょう。

今回の場合、二次元配列ですので二次元累積和を考えることもできますが、横軸方向を累積和で、縦軸方向をループで処理すればPyPyで十分間に合います。計算量はO(NQ)となります。

コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import itertools
N, M, Q = map(int, input().split())
P = []
LR = [[0] * (N+1) for _ in range(N+1)]
for m in range(M):
l, r = map(int, input().split())
LR[l][r] += 1

for q in range(Q):
p = list(map(int, input().split()))
P.append(p)

csum = []
for L in LR:
a = list(itertools.accumulate(L))
csum.append(a)

for p in P:
ans = 0
l, r = p
for c in csum[l:r+1]:
ans += (c[r] - c[l-1])
print (ans)

記事情報

  • 投稿日:2020年5月4日
  • 最終更新日:2020年5月8日

ABC166

はじめに

この記事では、AtCoder Beginer Contest 166のA~F問題を解説します。

A - A?C

https://atcoder.jp/contests/abc166/tasks/abc166_a

if文が簡単です。

1
2
3
4
5
S = input()
if S=='ABC':
print('ARC')
else:
print('ABC')

B - Trick or Treat

https://atcoder.jp/contests/abc166/tasks/abc166_b

setを使って、お菓子を持っているすぬけ君のユニーク数を数えます。

1
2
3
4
5
6
7
ls = []
N, K = map(int,input().split())
for k in range(K):
_ = input()
A = list(map(int,input().split()))
ls+=A
print (N-len(set(ls)))

C - Peaks

https://atcoder.jp/contests/abc166/tasks/abc166_c

M件の道情報それぞれに対して、良くない展望台が確定していきます。計算量はO(M)です。

隣接する展望台の高さが同じ場合も、良い展望台とはならないことに注意しましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
N, M = map(int,input().split())
ok = [1] * N
H = list(map(int,input().split()))
for m in range(M):
a, b = map(int,input().split())
a-=1
b-=1
if H[a] < H[b]:
ok[a] = 0
elif H[a] > H[b]:
ok[b] = 0
else:
ok[a] = ok[b] = 0
print (sum(ok))

D - I hate Factorization

https://atcoder.jp/contests/abc166/tasks/abc166_d

Aの符号とBの符号を(Aの符号, Bの符号)のように表現します。

  • Xは正のため、(-,+)のパターンを考慮する必要はない
  • (-,-)のパターンについては、A, Bの絶対値を入れ替えて符号を(+,+)とすることで等価なパターンが存在する。
    これらの考察からAを非負として扱っても一般性を失いません。

またA<=Bの時、A^5-B^5<=0となり制約を満たさなくなるので、A>Bとします。

ここから解説pdfのように考えます。

  • A=n, B=A-1すなわちn^5-(n-1)^5について考えます。
    先の議論からAが非負として、n^5-(n-1)^5は単調増加です。
    そしてn1ずつ増やして探索すると、n^5-(n-1)^510**9を超えるのはn=120の時です。すなわちAは119まで評価すればよく0<=A<=119となります。

  • A=n, B<A-1の場合について考えます。
    -B^5は単調減少であるため、BがA>Bの制約の元でどのような値をとっても、n^5-(n-1)^5A=12010**9を超えるはずなので、やはりAは119まで評価すれば十分です。

ここまでで

1
2
0<=A<=119
B<=118

です。

最後にBの下限について考えます。A=0の場合、すなわち-(B^5)についてB1ずつ小さくして探索するとB=-64で初めて10**9を超えます。先ほどと同様の議論で、Bの下限を-63としても問題ないことがわかります。

1
2
0<=A<=119
-63<=B<=118

この範囲であれば、全探索をしても全く問題がありません。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**7)
INF = 10**10
MOD = 10**9 + 7
X = int(input())
MAX_X = 10**9
a_max = 0
while(1):
a_max+=1
if a_max**5 - (a_max-1)**5 > MAX_X:
break
b_min = 0
while(1):
b_min-=1
if -(b_min)**5 > MAX_X:
break
#print (a_max, b_min)
for i in range(a_max):
for j in range(b_min+1,a_max-1):
if i**5 - j**5 == X:
print (i,j)
exit()

E - This Message Will Self-Destruct in 5s

https://atcoder.jp/contests/abc166/tasks/abc166_e

身長+番号(A[i]+(i+1))と 身長-番号(A[i]-(i+1))を事前に計算しておき、引いて0になる組み合わせを数え上げれば良いです。

前者のパターンを辞書として作成し、後者のパターンのクエリをループすれば良いです。

ここで、

  • ペアの組み合わせではなく、順列を考えている
  • 自身と比較しても、身長が正であることから条件を満たし得ない
  • 番号の差の絶対値ではなく、番号の差を考えている。

自身以外との番号は必ず異なる上、番号は非負なので、順列(p,q)の番号の差p-qと順列(q,p)の番号の差q-pは必ず異なる。そのため、各ペアに対して重複してカウントする心配はない。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
N = int(input())
A = list(map(int,input().split()))

minus = []
plus = []
for i in range(len(A)):
minus.append(A[i]-(i+1))
plus.append(A[i]+(i+1))

d = {}
for m in minus:
d[m] = d.get(m,0) + 1 # 初期を0としてカウントアップ

ans = 0
for p in plus:
ans += d.get(-p,0) # 足して0になる(符号を反転させて等しい)ものをカウント
print (ans)

F - Three Variables Game

https://atcoder.jp/contests/abc166/tasks/abc166_f

解説pdfではA+B+Cの値に着目している。

クエリsiの文字列をL,Rとしてカウンタd[L]に着目しても解くことが出来る。

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
S = []
history = []
N, A, B ,C = map(int,input().split())
d = {}
d['A'] = A
d['B'] = B
d['C'] = C

for n in range(N):
s = input()
S.append(s)

def select(L,R,x): # xを選択した場合の処理
diff = 1
if x==R:
diff = -1
d[L] += diff
d[R] -= diff
history.append(x)

for n in range(N):
L, R = S[n]
if d[L] >= 2: # 2以上なら無条件で引いて良い
select(L,R,R)
elif d[L] == 0: # 0なら無条件で足すしかない。それができないならNo
if d[R] == 0:
print ('No')
exit()
else:
select(L,R,L)
elif d[L] == 1:
if d[R] >= 2: # 2以上なら無条件で引いて良い
select(L,R,L)
elif d[R] == 0: # 0なら無条件で足すしかない
select(L,R,R)
elif d[R] == 1:
if n!=N-1: # 最後のクエリでない場合
if L in S[n+1]: # 次のクエリに出現する方に足しておく
select(L,R,L)
else:
select(L,R,R)
else: # 最後のクエリなら
select(L,R,L) # どちらでもOK
print ('Yes')
for h in history:
print (h)

関連記事

過去のABC解説記事です。

  • ABC165
    • A-F問題を解いています。
  • ABC164
    • A-E問題を解いています。
  • ABC163
    • A-D問題を解いています。
  • ABC162
    • A-E問題を解いています。

記事情報

  • 投稿日:2020年5月4日
  • 最終更新日:2020年5月4日

ABC021D 多重ループ

この記事で使うアルゴリズム

逆元

はじめに

カテゴリー競プロ初中級者100問では、Qiitaにて@e869120さんがレッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】としてまとめられている100問をPythonで解いています。

全問題の一覧はこちらです

問題

https://atcoder.jp/contests/abc021/tasks/abc021_d

この問題では逆元の計算が求められます。

方針

準備運動として、下記の組み合わせの数え上げについてを考えます。

1
1 <= a_1 < a_2 < ... < a_k <= N

少し考察すると、これはN個の要素の中から重複せずにk個の要素を取り出すパターン数、すなわちn_C_kと等価であることがわかります。

そのように理解すると、本題の

1
1 <= a_1 <= a_2 <= ... <= a_k <= N

N個の要素の中から重複を許してk個の要素を取り出すパターン数であることがわかります。これは重複組合せと呼ばれる概念で、

1
n_H_k = (n-1+k)_C_k = (n-1+k)!/((n-1)!k!)

を計算すれば良いです。

階乗の部分が巨大になるため、計算途中で剰余の計算を行います。しかしながら、除算が含まれるため逆元と呼ばれる概念を導入する必要があります。

逆元については以下の問題の解説をご覧ください。

ABC034C 散歩

コード

1
2
3
4
5
6
7
8
9
10
11
MOD = 10**9+7
n = int(input())
k = int(input())
MX = 2 * 10**5 # n+k-1が最大となる場合に対応
fact = [1] * (MX+1) # 階乗を格納するリスト
factinv = [1] * (MX+1) # 階乗の逆元を格納するリスト
for i in range(MX):
fact[i+1] = fact[i] * (i+1) % MOD # 階乗を計算
factinv[i+1] = pow(fact[i+1], MOD-2, MOD)# MODを法とした逆元
ans = fact[n+k-1] * factinv[k] * factinv[n-1]
print (ans%MOD) # 最後にMODを忘れずに

記事情報

  • 投稿日:2020年5月3日
  • 最終更新日:2020年5月8日