SlideShare a Scribd company logo
1 of 50
Download to read offline
JOI 2019/2020
一次予選 第1回 最速非公式解説
きたむー (@Pro_ktmr)
A – 3つの整数
Three Integers
JOI 2019/2020 一次予選① 最速非公式解説
A 問題概要
• 3 つの整数 𝐴, 𝐵, 𝐶 が与えられ
る
• 𝐴, 𝐵, 𝐶 はいずれも 1 または
2 である
• 1 と 2 のうち,どちらが多くあ
るか?
JOI 2019/2020 一次予選① 最速非公式解説
問題文の公開は許可を頂いています
A 考察①
• if文を用いた場合分けを考える
• 愚直に場合分けすると,
𝐴, 𝐵, 𝐶 = 1,1,1 , 1,1,2 , ⋯ , 2,2,2 と,8 通りも
あって大変
• できなくはないがエレガントな解法ではない
• 数が「1」または「2」であることをうまく利用できない
か?
JOI 2019/2020 一次予選① 最速非公式解説
A 考察②
• 𝐴, 𝐵, 𝐶 の和 𝐴 + 𝐵 + 𝐶 に着目してみる
• 解説者は平均を求める際に和を取ることなどから発想
• 1 が 3 個,2 が 0 個 𝐴 + 𝐵 + 𝐶 = 3
• 1 が 2 個,2 が 1 個 𝐴 + 𝐵 + 𝐶 = 4
• 1 が 1 個,2 が 2 個 𝐴 + 𝐵 + 𝐶 = 5
• 1 が 0 個,2 が 3 個 𝐴 + 𝐵 + 𝐶 = 6
• よって, 𝐴 + 𝐵 + 𝐶 ≧ 5 であれば 2 が多く, 𝐴 + 𝐵 +
𝐶 < 5 であれば 1 の方が多い (問題の同値変形)
JOI 2019/2020 一次予選① 最速非公式解説
A 実装例
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int A, B, C;
cin >> A >> B >> C;
if(A+B+C >= 5){
cout << 2 << endl;
}
else{
cout << 1 << endl;
}
}
Python3
a,b,c = map(int,input().split())
if a+b+c >= 5:
print(2)
else:
print(1)
JOI 2019/2020 一次予選① 最速非公式解説
変数の宣言
入力の受け取り
条件分岐と
答えの出力
入力の受け取り
条件分岐と
答えの出力
B – 母音を数える
Counting Vowels
JOI 2019/2020 一次予選① 最速非公式解説
B 問題概要
• 長さ 𝑁 の英小文字からなる文字
列 𝑆 が与えられる
• 𝑆 のうち母音字(a,i,u,e,o)の個
数を求めよ
JOI 2019/2020 一次予選① 最速非公式解説
問題文の公開は許可を頂いています
B 考察
【文字列問題の常識】
• 文字列全体を一度に処理することは難しい
• ループを回して 1 文字ずつ着目していく
【本問の考察】
• 母音を数えるためのカウンター変数を用意する
• 着目している文字が母音ならカウンターを加算する
JOI 2019/2020 一次予選① 最速非公式解説
B 実装の前に
文字列の扱い方を確認しよう
【C++】
• 文字列はstring型,文字はchar型
• 文字列から 𝑖 文字目 0 ≦ 𝑖 < 𝑁 だけを取り出すに
は 𝑆[𝑖] とする
• 0 文字目から始まることに注意
• 文字は'a'のようにシングルクオーテーションで囲む
Pythonは省略
JOI 2019/2020 一次予選① 最速非公式解説
B 実装例
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int N;
string S;
cin >> N >> S;
int counter = 0;
for(int i=0; i<N; i++){
if(S[i] == 'a') counter++;
if(S[i] == 'i') counter++;
if(S[i] == 'u') counter++;
if(S[i] == 'e') counter++;
if(S[i] == 'o') counter++;
}
cout << counter << endl;
}
Python3
n = int(input())
s = input()
counter = 0
for c in s:
if c == 'a':
counter += 1
if c == 'i':
counter += 1
if c == 'u':
counter += 1
if c == 'e':
counter += 1
if c == 'o':
counter += 1
print(counter)
JOI 2019/2020 一次予選① 最速非公式解説
文字列はstring型
ループはfor文
文字は''で囲む
ループはfor文
Pythonでは
文字と文字列の
区別がない
C – マージ
Merge
JOI 2019/2020 一次予選① 最速非公式解説
C 問題概要
• 共に広義単調増加な長さ 𝑁 の数列
𝐴 と長さ 𝑀 の数列 𝐵 が与えられる
• 空の数列 𝐶 を用意する
• 𝐴 と 𝐵 の先頭のうち小さい方を 𝐶
の末尾に追加し,その先頭の要素を
削除する
• 𝐶 の要素数は最終的に 𝑁 + 𝑀 にな
るが,そのときの中身を出力せよ
JOI 2019/2020 一次予選① 最速非公式解説 問題文の公開は許可を頂いています
C 実装例
考察
• 問題文に書かれている操
作をそのまま実装すれば
よい
• 数列は「配列」を用いて
実装する
• 数列 𝐴, 𝐵 ともに,今の先
頭はどこかという情報を
変数 𝑖, 𝑗 で持つ
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int N, M, A[500], B[500];
cin >> N >> M;
for(int i=0; i<N; i++) cin >> A[i];
for(int j=0; j<M; j++) cin >> B[j];
int i = 0;
int j = 0;
for(int k=0; k<N+M; k++){
if(i == N){
cout << B[j] << endl;
j++;
}
else if(j == M){
cout << A[i] << endl;
i++;
}
else{
if(A[i] <= B[j]){
cout << A[i] << endl;
i++;
}
else{
cout << B[j] << endl;
j++;
}
}
}
}
JOI 2019/2020 一次予選① 最速非公式解説
実装が大変だけど我慢しよう!
・・・本当に?
C 考察
• ちょっと待った!!!
• 前ページは正しいが,もっとエレガントな解法がある
• 出力例を見ると,広義単調増加になっている
• よくよく考えると,小さい方を選んでいるから当たり前
• 実はこのアルゴリズムは「マージソート」というソート
アルゴリズムの一部である
• 問題名「マージ」もここからきている
• よって,数列 𝐴, 𝐵 をごちゃまぜにして,ソートしたも
のを出力すればよい
JOI 2019/2020 一次予選① 最速非公式解説
少し立ち止まって,問題の意味を
考えてみることも大切!
C 実装例
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int N, M, C[1000];
cin >> N >> M;
for(int i=0; i<N+M; i++){
cin >> C[i];
}
sort(C, C+N+M);
for(int i=0; i<N+M; i++){
cout << C[i] << endl;
}
}
Python3
n, m = map(int,input().split())
c = list(map(int,input().split()))
c += list(map(int,input().split()))
c.sort()
for c_i in c:
print(c_i)
JOI 2019/2020 一次予選① 最速非公式解説
最初から要素数
𝑁 + 𝑀 の数列 𝐶
だけを考える
ソート(並び替え)は
書き方に注意
最初から要素数
𝑁 + 𝑀 の数列 𝐶
だけを考える
【Tips】
マージソートは再帰的な処理や分割統治の仕組みに
ついて理解するのに良い例だ
今回の3問が解けた人は是非挑戦してみよう!
▼おすすめリンク
• https://www.codereading.com/algo_an
d_ds/algo/merge_sort.html
• https://onlinejudge.u-
aizu.ac.jp/problems/ALDS1_5_B
JOI 2019/2020
一次予選 第2回 最速非公式解説
きたむー (@Pro_ktmr)
A – 試験
Exam
JOI 2019/2020 一次予選② 最速非公式解説
A 問題概要
• 3 つの整数 𝐴, 𝐵, 𝐶 が与えられる
• 𝐴, 𝐵, 𝐶 のうち高い方から 2 つを足
し合わせた合計は?
JOI 2019/2020 一次予選② 最速非公式解説
A 考察①
• まずは if 文を用いた場合分けを考える
• 愚直に場合分けすると,
𝐴 ≧ 𝐵 ≧ 𝐶, 𝐴 ≧ 𝐵 ≧ 𝐶, 𝐵 ≧ 𝐴 ≧ 𝐶, 𝐵 ≧ 𝐶 ≧ 𝐴,
𝐶 ≧ 𝐴 ≧ 𝐵, 𝐶 ≧ 𝐵 ≧ 𝐴 と,6 通りもあって大変
• できなくはないがエレガントな解法ではない
• 求める値「大きい方から 2 つの合計」をうまく言い換
えられないか?
JOI 2019/2020 一次予選② 最速非公式解説
A 考察②
• 「大きい方から 2 つの合計」は「3 つの合計から最小
値を引いたもの」と言い換えられる (問題の同値変形)
• この言い換えは既に出題されている
「科目選択」(2015/2016 予選 A)など
• 最小値は場合分けではなく,「仮定して更新」という
考え方を用いて求められる
1. 最小値を 𝑚 とおき,𝐴 が最小と仮定して 𝑚 = 𝐴 とする
2. もし 𝑚 > 𝐵 なら最小値を 𝑚 = 𝐵 で更新する
3. もし 𝑚 > 𝐶 なら最小値を 𝑚 = 𝐶 で更新する
JOI 2019/2020 一次予選② 最速非公式解説
A 実装例
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int A, B, C;
cin >> A >> B >> C;
int m = A;
if(m > B){
m = B;
}
if(m > C){
m = C;
}
cout << A + B + C - m << endl;
}
Python3
a,b,c = map(int,input().split())
m = a
if m > b:
m = b
if m > c:
m = c
print(a + b + c - m)
JOI 2019/2020 一次予選② 最速非公式解説
変数の宣言
入力の受け取り
最小値の計算
入力の受け取り
最小値の計算
【Tips】
最小値は様々な方法で求めることが出来る
・ここで紹介した方法は要素数が大きくなっても対
応しやすい方法だ
・他にも C++ であれば min 関数を用いて
int m = min({A, B, C});
などとすることもできる
B – 文字列の反転
Inversion of a String
JOI 2019/2020 一次予選② 最速非公式解説
B 問題概要
• 長さ 𝑁 の文字列 𝑆 が与えられる
• 𝑆 の 𝐴 文字目から 𝐵 文字目まで
の並びを逆にした文字列は?
• ただし,𝑆 の先頭は 1 文字目で
末尾が 𝑁 文字目
JOI 2019/2020 一次予選② 最速非公式解説
J O I j o i J o I j
J O J i o j I o I j
B 考察の前に
文字列の扱い方を確認しよう
【C++】
• 文字列は string 型
• 文字列から 𝑖 文字目 0 ≦ 𝑖 < 𝑁 だけを取り出すに
は 𝑆[𝑖] とする
• 𝟎 文字目から始まることに注意
Python は省略
JOI 2019/2020 一次予選② 最速非公式解説
0 1 2 3 4 5 6 7 8 9
J O I j o i J o I j
▲ 𝐴 − 1 文字目 ▲ 𝐵 − 1 文字目
B 考察
【文字列問題の常識】
• 文字列全体を一度に処理することは難しい
• ループを回して 1 文字ずつ着目していく
【本問の考察】
• 出来上がった文字列は「もとのまま」「反転している」
「もとのまま」と 3 つの部分からなっている
• 「反転している」の部分はループを逆向きに回す
• つまり 0 → 𝐴 − 2, 𝐵 − 1 → 𝐴 − 1, 𝐵 → 𝑁 − 1
JOI 2019/2020 一次予選② 最速非公式解説
B 実装例
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int N, A ,B;
string S;
cin >> N >> A >> B >> S;
for(int i=0; i<=A-2; i++){
cout << S[i];
}
for(int i=B-1; i>=A-1; i--){
cout << S[i];
}
for(int i=B; i<=N-1; i++){
cout << S[i];
}
cout << endl;
}
Python3
n,a,b = map(int,input().split())
s = input()
i = 0
while i<=a-2:
print(s[i], end="")
i += 1
i = b-1
while i >=a-1:
print(s[i], end="")
i -= 1
i = b
while i <= n-1:
print(s[i], end="")
i += 1
JOI 2019/2020 一次予選② 最速非公式解説
文字列はstring型
ループはfor文
逆向きにループを
回すときは不等号
の向きとi--に注意
複雑なループは
while文
改行無しで出力す
るときはend=""
【Tips】
reverse関数を用いれば簡単に文字列を反転させ
ることが出来る (C++の場合は下)
reverse(S.begin()+A-1, S.begin()+B);
for文で逆向きの
ループを回す方法
も調べてみよう!
C – 最頻値
Mode
JOI 2019/2020 一次予選② 最速非公式解説
C 問題概要
• 長さ 𝑁 の数列 𝐴 が与えられる
• 1 ≦ 𝐴𝑖 ≦ 𝑀 ≦ 100
• 長さ 𝑀 の数列 𝐵 を次のように定
義する:
𝐵𝑗 の値は 𝐴𝑖 = 𝑗 である 𝑖 の個数
• 数列 𝐵 の最大値は?
JOI 2019/2020 一次予選② 最速非公式解説
C 実装例
考察
• 問題文に書かれている定
義をそのまま実装すれば
よい
• 数列は「配列」を用いて
実装する
• 最大値 𝑀 は問題 A で紹
介した方法で求められる
• 数列と配列のずれに注意
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int N, M, A[100], B[101];
cin >> N >> M;
for(int i=0; i<N; i++) cin >> A[i];
for(int j=1; j<=M; j++){
B[j] = 0;
for(int i=0; i<N; i++){
if(A[i] == j) B[j]++;
}
}
int m = 0;
for(int j=1; j<=M; j++){
if(m > B[j]) m = B[j];
}
cout << m << endl;
}
JOI 2019/2020 一次予選② 最速非公式解説
実装が大変だけど,前回より
ましだから我慢しよう!
・・・本当に?
C 考察
• ちょっと待った!!!
• 前ページは正しいが,数列 𝐵 はもっとエレガントな方
法で求められる
• 𝐵𝑗 は数列 𝐴 中の 𝑗 の登場回数だから,𝐴 の視点から
見て,𝐵𝐴 𝑖
に 1 ずつ加算していけばよい
• 意味不明だと思うので実装例要参照
• 数列 𝐵 を先に 0 で初期化しておくことを忘れずに
JOI 2019/2020 一次予選② 最速非公式解説
C 実装例
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int N, M, B[101];
cin >> N >> M;
for(int j=1; j<=M; j++) B[j] = 0;
for(int i=0; i<N; i++){
int A_i;
cin >> A_i;
B[A_i]++;
}
int m = 0;
for(int j=1; j<=M; j++){
if(m < B[j]) m = B[j];
}
cout << m << endl;
}
Python3
n,m = map(int,input().split())
a = list(map(int,input().split()))
b = [0] * 101
for a_i in a:
b[a_i] += 1
answer = 0
for b_j in b:
if answer < b_j:
answer = b_j
print(answer)
JOI 2019/2020 一次予選② 最速非公式解説
数列 𝐵 を 0 で
初期化しておく
𝐵 𝐴 𝑖
に 1 加算
最大値は問題 A の
方法で求める
数列 𝐵 を 0 で
初期化しておく
𝐵 𝐴 𝑖
に 1 加算
最大値は問題 A の
方法で求める
【Tips】
最大値,最小値はsort関数を用いて求めることも出
来るが,計算に少し時間がかかる
Pythonの場合はmax関数,min関数が使える
JOI 2019/2020
一次予選 第3回 最速非公式解説
きたむー (@Pro_ktmr)
A – Xに最も近い値
The Nearest Value
JOI 2019/2020 一次予選③ 最速非公式解説
A 問題概要
• 3 つの整数 𝑋, 𝐿, 𝑅 が与えら
れる
• 𝐿 以上 𝑅 以下の整数のうち,
𝑋 との差の絶対値が最も小さ
い値は?
JOI 2019/2020 一次予選③ 最速非公式解説
A 考察①
• 「差の絶対値が小さい」とは何か
• これは数直線上の 2 数の距離を表すと考えられる
• つまり,𝑋 と数直線上の距離が最も近い数
𝑎 (𝐿 ≦ 𝑎 ≦ 𝑅) が答え(問題の同値変形)
JOI 2019/2020 一次予選③ 最速非公式解説
𝑋 𝑎
|𝑋 − 𝑎|
A 考察②
• 𝑋 と区間 𝐿~𝑅 の位置関係で場合分けする
• これを if 文を用いて実装すればよい
JOI 2019/2020 一次予選③ 最速非公式解説
(i) 𝑿 が区間の左側
• 数式で表すと,
𝑋 < 𝐿 のとき
• 図より,答えは 𝐿
(ii) 𝑿 が区間内
• 数式で表すと,
𝐿 ≦ 𝑋 ≦ 𝑅 のとき
• 図より,答えは 𝑋
(iii) 𝑿 が区間の右側
• 数式で表すと,
𝑅 < 𝑋 のとき
• 図より,答えは 𝑅
𝑋 𝐿 𝑅
𝑎
𝐿 𝑋 𝑅
𝑎
𝐿 𝑅 𝑋
𝑎
A 実装例
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int X, L, R;
cin >> X >> L >> R;
if(X < L){
cout << L << endl;
}
if(L <= X && X <= R){
cout << X << endl;
}
if(R < X){
cout << R << endl;
}
}
Python3
x,l,r = map(int,input().split())
if x < l:
print(l)
if l <= x <= r:
print(x)
if r < x:
print(r)
JOI 2019/2020 一次予選③ 最速非公式解説
変数の宣言
入力の受け取り
場合分けの実装
入力の受け取り
場合分けの実装
条件式の「かつ」は &&
L <= X <= R とは
できないので注意!
B – キャピタリゼーション
Capitalization
JOI 2019/2020 一次予選③ 最速非公式解説
B 問題概要
• 長さ 𝑁 の文字列 𝑆 が与えられる
• 𝑆 はすべて英小文字である
• 𝑆 に含まれる連続する文字列
'joi' をすべて 'JOI' に置き換
えた文字列は?
JOI 2019/2020 一次予選③ 最速非公式解説
j o i n o j o i j i n
J O I n o J O I j i n
B 考察の前に
文字列の扱い方を確認しよう
【C++】
• 文字列は string 型,文字は char 型
• 文字は'a'のようにシングルクオーテーションで囲む
• 文字列から 𝑖 文字目 0 ≦ 𝑖 < 𝑁 だけを取り出すに
は 𝑆[𝑖] とする
• 𝟎 文字目から始まることに注意
JOI 2019/2020 一次予選③ 最速非公式解説
0 1 2 3 4 5 6 7 8 9 10
j o i n o j o i j i n
B 考察
【文字列問題の常識】
• 文字列全体を一度に処理することは難しい
• ループを回して 1 文字ずつ着目していく
【本問の考察】
• 今見ている文字を 𝑆[𝑖] として, 𝑆 𝑖 = 'j' かつ
𝑆 𝑖 + 1 = 'o' かつ 𝑆 𝑖 + 2 = 'i' であれば,
それぞれ 'J' 'O' 'I' に置き換える
• ループを 0~𝑁 − 1 で回すとはみ出すので注意
JOI 2019/2020 一次予選③ 最速非公式解説
N-3 N-2 N-1
… o j o ない
𝑖 = 𝑁 − 2 のとき
B 実装例
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int N;
string S;
cin >> N >> S;
for(int i=0; i+2<N; i++){
if(S[i] == 'j' && S[i+1] == 'o'
&& S[i+2] == 'i'){
S[i] = 'J';
S[i+1] = 'O';
S[i+2] = 'I';
}
}
cout << S << endl;
}
Python3
n = int(input())
s = input()
print(s.replace('joi', 'JOI'))
JOI 2019/2020 一次予選③ 最速非公式解説
文字列はstring型
ループはfor文
𝑆[𝑖 + 2] が 𝑁 文字
に含まれるよう,
条件式を設定
replace 関数を
用いた
【Tips】 Python における関数の利用
Python には C++ と比べて様々な関数が用意さ
れており,それらを用いれば簡単に問題が解けるこ
ともある
ただし,高みを目指すのであれば,その関数の中で
どのような処理が行われているのか,しっかり把握
しておこう
C – 最長昇順連続部分列
Longest Ascending Contiguous Subsequence
JOI 2019/2020 一次予選③ 最速非公式解説
C 問題概要
• 長さ 𝑁 の数列 𝐴 が与えられる
• 1 ≦ 𝑁 ≦ 100
• 数列 𝐴 の連続部分列の中で昇
順に並んでいるもののうち最長
のものの長さは?
• すなわち,𝐴𝑙 ≦ 𝐴𝑙+1 ≦ ⋯ ≦ 𝐴 𝑟
を満たすような 2 整数 𝑙, 𝑟 につ
いて, 𝑟 − 𝑙 + 1 の最大値は?
JOI 2019/2020 一次予選③ 最速非公式解説
C 実装例
考察
• 問題文にわざわざ数式
や文字 l, r を用いて条
件が書かれているから,
そのまま実装する
• 数列は「配列」を用いて
実装する
• 最大値 𝑎𝑛𝑠 は前回紹介し
た方法で求められる
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int N, A[100];
cin >> N;
for(int i=0; i<N; i++) cin >> A[i];
int ans = 0;
for(int l=0; l<N; l++){
for(int r=l; r<N; r++){
int ok = 1;
for(int i=l+1; i<=r; i++){
if(A[i-1] > A[i]) ok = 0;
}
if(ok == 1 && ans < r-l+1)
ans = r-l+1;
}
}
cout << ans << endl;
}
JOI 2019/2020 一次予選③ 最速非公式解説
実装が大変だけど,我慢しよう!
・・・本当に?
C 考察
• ちょっと待った!!!
• 前ページは正しいが,もっとエレガントな解法がある
• 前ページはすべての組 (𝑙, 𝑟) について,昇順か確か
めているが,その必要はない
• 任意の 1 要素は昇順だから答えは絶対に 1 以上
• 最初は 𝑟 = 𝑙 + 1 として,𝑟 を 1 ずつ大きくしていき,
その都度昇順が維持されているか確認すればよい
• 昇順が維持されなくなったらその 𝑙 はもう無理
JOI 2019/2020 一次予選③ 最速非公式解説
C 実装例
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int N, A[100];
cin >> N;
for(int i=0; i<N; i++) cin >> A[i];
int ans = 1;
for(int l=0; l<N; l++){
for(int r=l+1; r<N; r++){
if(A[r-1] > A[r]) break;
if(ans < r-l+1) ans = r-l+1;
}
}
cout << ans << endl;
}
Python3
n = int(input())
a = list(map(int,input().split()))
ans = 1
for l in range(n):
for r in range(n):
if l >= r:
continue
if a[r-1] > a[r]:
break
if ans < r-l+1:
ans = r-l+1
print(ans)
JOI 2019/2020 一次予選③ 最速非公式解説
昇順が維持されなく
なったらループ終了
break; を使う
最大値は前回紹介し
た方法で求める
最大値は前回紹介し
た方法で求める
昇順が維持されなく
なったらループ終了
break を使う
𝑟 = 𝑙 + 1 となるまで
continue
を使って待つ
C 別解考察
• 与えられた数列の隙間に,その 2 数の大小関係 (≦,
>) を書いてみる (下は入力例 3)
• このとき,当然 ≦ が連続して並ぶ個数の最大値+1
が答え
• 前から,今 ≦ が何回続いているか更新しつつ処理
JOI 2019/2020 一次予選③ 最速非公式解説
1 2 2 12 120 210 202 1010 2020
≦ ≦ ≦ ≦ ≦ > ≦ ≦
C 別解実装例
C++
#include"bits/stdc++.h"
using namespace std;
int main(){
int N, A[100];
cin >> N;
for(int i=0; i<N; i++) cin >> A[i];
int ans = 0;
int now = 0;
for(int i=0; i+1<N; i++){
if(A[i] <= A[i+1]) now++;
else now = 0;
ans = max(ans, now);
}
cout << ans+1 << endl;
}
Python3
n = int(input())
a = list(map(int,input().split()))
ans = 0
now = 0
for i in range(n-1):
if a[i] <= a[i+1]:
now += 1
else:
now = 0
ans = max(ans, now)
print(ans+1)
JOI 2019/2020 一次予選③ 最速非公式解説
最大値はmax関数を
使っても求められる
今 ≦ が何回続いて
いるか管理する
最大値はmax関数を
使っても求められる
今 ≦ が何回続いて
いるか管理する
【Tips】 ループの回数
この問題では計 3 つの解法を紹介したが,最初の
解法は 3 重ループ,標準解法は 2 重ループ,別解
は 1 重ループを用いた
各ループの回る回数が同じとき,〇重の〇が小さい
ほど処理が高速で,性能の良いプログラムとなる
今回の問題が解けた人は,「時間計算量」についてよ
り深く学んでみよう!

More Related Content

What's hot

AtCoder Regular Contest 028 解説
AtCoder Regular Contest 028 解説AtCoder Regular Contest 028 解説
AtCoder Regular Contest 028 解説AtCoder Inc.
 
AtCoder Regular Contest 025 解説
AtCoder Regular Contest 025 解説AtCoder Regular Contest 025 解説
AtCoder Regular Contest 025 解説AtCoder Inc.
 
AtCoder Regular Contest 002
AtCoder Regular Contest 002AtCoder Regular Contest 002
AtCoder Regular Contest 002AtCoder Inc.
 
AtCoder Regular Contest 042 解説
AtCoder Regular Contest 042 解説AtCoder Regular Contest 042 解説
AtCoder Regular Contest 042 解説AtCoder Inc.
 
2SAT(充足可能性問題)の解き方
2SAT(充足可能性問題)の解き方2SAT(充足可能性問題)の解き方
2SAT(充足可能性問題)の解き方Tsuneo Yoshioka
 
AtCoder Beginner Contest 025 解説
AtCoder Beginner Contest 025 解説AtCoder Beginner Contest 025 解説
AtCoder Beginner Contest 025 解説AtCoder Inc.
 
AtCoder Beginner Contest 006 解説
AtCoder Beginner Contest 006 解説AtCoder Beginner Contest 006 解説
AtCoder Beginner Contest 006 解説AtCoder Inc.
 
AtCoder Beginner Contest 029 解説
AtCoder Beginner Contest 029 解説AtCoder Beginner Contest 029 解説
AtCoder Beginner Contest 029 解説AtCoder Inc.
 
Code Formula 予選B 解説
Code Formula 予選B 解説Code Formula 予選B 解説
Code Formula 予選B 解説AtCoder Inc.
 
AtCoder Regular Contest 030 解説
AtCoder Regular Contest 030 解説AtCoder Regular Contest 030 解説
AtCoder Regular Contest 030 解説AtCoder Inc.
 
AtCoder Beginner Contest 021 解説
AtCoder Beginner Contest 021 解説AtCoder Beginner Contest 021 解説
AtCoder Beginner Contest 021 解説AtCoder Inc.
 
AtCoder Beginner Contest 013 解説
AtCoder Beginner Contest 013 解説AtCoder Beginner Contest 013 解説
AtCoder Beginner Contest 013 解説AtCoder Inc.
 
AtCoder Beginner Contest 015 解説
AtCoder Beginner Contest 015 解説AtCoder Beginner Contest 015 解説
AtCoder Beginner Contest 015 解説AtCoder Inc.
 
Donutsプロコンチャレンジ 2015 解説
Donutsプロコンチャレンジ 2015 解説Donutsプロコンチャレンジ 2015 解説
Donutsプロコンチャレンジ 2015 解説kuno4n
 
AtCoder Beginner Contest 023 解説
AtCoder Beginner Contest 023 解説AtCoder Beginner Contest 023 解説
AtCoder Beginner Contest 023 解説AtCoder Inc.
 
AtCoder Regular Contest 037 解説
AtCoder Regular Contest 037 解説AtCoder Regular Contest 037 解説
AtCoder Regular Contest 037 解説AtCoder Inc.
 

What's hot (20)

AtCoder Regular Contest 028 解説
AtCoder Regular Contest 028 解説AtCoder Regular Contest 028 解説
AtCoder Regular Contest 028 解説
 
abc032
abc032abc032
abc032
 
AtCoder Regular Contest 025 解説
AtCoder Regular Contest 025 解説AtCoder Regular Contest 025 解説
AtCoder Regular Contest 025 解説
 
AtCoder Regular Contest 002
AtCoder Regular Contest 002AtCoder Regular Contest 002
AtCoder Regular Contest 002
 
Abc009
Abc009Abc009
Abc009
 
AtCoder Regular Contest 042 解説
AtCoder Regular Contest 042 解説AtCoder Regular Contest 042 解説
AtCoder Regular Contest 042 解説
 
2SAT(充足可能性問題)の解き方
2SAT(充足可能性問題)の解き方2SAT(充足可能性問題)の解き方
2SAT(充足可能性問題)の解き方
 
AtCoder Beginner Contest 025 解説
AtCoder Beginner Contest 025 解説AtCoder Beginner Contest 025 解説
AtCoder Beginner Contest 025 解説
 
AtCoder Beginner Contest 006 解説
AtCoder Beginner Contest 006 解説AtCoder Beginner Contest 006 解説
AtCoder Beginner Contest 006 解説
 
AtCoder Beginner Contest 029 解説
AtCoder Beginner Contest 029 解説AtCoder Beginner Contest 029 解説
AtCoder Beginner Contest 029 解説
 
Code Formula 予選B 解説
Code Formula 予選B 解説Code Formula 予選B 解説
Code Formula 予選B 解説
 
AtCoder Regular Contest 030 解説
AtCoder Regular Contest 030 解説AtCoder Regular Contest 030 解説
AtCoder Regular Contest 030 解説
 
AtCoder Beginner Contest 021 解説
AtCoder Beginner Contest 021 解説AtCoder Beginner Contest 021 解説
AtCoder Beginner Contest 021 解説
 
AtCoder Beginner Contest 013 解説
AtCoder Beginner Contest 013 解説AtCoder Beginner Contest 013 解説
AtCoder Beginner Contest 013 解説
 
AtCoder Beginner Contest 015 解説
AtCoder Beginner Contest 015 解説AtCoder Beginner Contest 015 解説
AtCoder Beginner Contest 015 解説
 
C : 解説
C : 解説C : 解説
C : 解説
 
Donutsプロコンチャレンジ 2015 解説
Donutsプロコンチャレンジ 2015 解説Donutsプロコンチャレンジ 2015 解説
Donutsプロコンチャレンジ 2015 解説
 
abc031
abc031abc031
abc031
 
AtCoder Beginner Contest 023 解説
AtCoder Beginner Contest 023 解説AtCoder Beginner Contest 023 解説
AtCoder Beginner Contest 023 解説
 
AtCoder Regular Contest 037 解説
AtCoder Regular Contest 037 解説AtCoder Regular Contest 037 解説
AtCoder Regular Contest 037 解説
 

Similar to 【解説】JOI 2019/2020 一次予選 最速非公式解説【競技プログラミング】

マルチコアを用いた画像処理
マルチコアを用いた画像処理マルチコアを用いた画像処理
マルチコアを用いた画像処理Norishige Fukushima
 
kagami_comput2015_8
kagami_comput2015_8kagami_comput2015_8
kagami_comput2015_8swkagami
 
Indeedなう 予選A 解説
Indeedなう 予選A 解説Indeedなう 予選A 解説
Indeedなう 予選A 解説AtCoder Inc.
 
C言語ポインタ講座 (Lecture of Pointer in C)
C言語ポインタ講座 (Lecture of Pointer in C)C言語ポインタ講座 (Lecture of Pointer in C)
C言語ポインタ講座 (Lecture of Pointer in C)kakira9618
 
8_C言語入門 - 条件分岐について(if-else if-else)
8_C言語入門 - 条件分岐について(if-else if-else)8_C言語入門 - 条件分岐について(if-else if-else)
8_C言語入門 - 条件分岐について(if-else if-else)bc_rikko
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるHideyuki Tanaka
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門natrium11321
 
動的計画法の並列化
動的計画法の並列化動的計画法の並列化
動的計画法の並列化Proktmr
 
第15回 配信講義 計算科学技術特論B(2022)
第15回 配信講義 計算科学技術特論B(2022)第15回 配信講義 計算科学技術特論B(2022)
第15回 配信講義 計算科学技術特論B(2022)RCCSRENKEI
 
AtCoder Regular Contest 026 解説
AtCoder Regular Contest 026 解説AtCoder Regular Contest 026 解説
AtCoder Regular Contest 026 解説AtCoder Inc.
 
El text.tokuron a(2019).watanabe190613
El text.tokuron a(2019).watanabe190613El text.tokuron a(2019).watanabe190613
El text.tokuron a(2019).watanabe190613RCCSRENKEI
 
TeamLabLT20120630
TeamLabLT20120630TeamLabLT20120630
TeamLabLT20120630tayama0324
 
kagamicomput201808
kagamicomput201808kagamicomput201808
kagamicomput201808swkagami
 
AtCoder Beginner Contest 010 解説
AtCoder Beginner Contest 010 解説AtCoder Beginner Contest 010 解説
AtCoder Beginner Contest 010 解説AtCoder Inc.
 
AtCoder Beginner Contest 019 解説
AtCoder Beginner Contest 019 解説AtCoder Beginner Contest 019 解説
AtCoder Beginner Contest 019 解説AtCoder Inc.
 
Pythonで始めた数値計算の授業@わんくま勉強会2018-04
Pythonで始めた数値計算の授業@わんくま勉強会2018-04Pythonで始めた数値計算の授業@わんくま勉強会2018-04
Pythonで始めた数値計算の授業@わんくま勉強会2018-04Katsuhiro Morishita
 
超LINQ入門
超LINQ入門超LINQ入門
超LINQ入門yone64
 
kagami_comput2016_08
kagami_comput2016_08kagami_comput2016_08
kagami_comput2016_08swkagami
 

Similar to 【解説】JOI 2019/2020 一次予選 最速非公式解説【競技プログラミング】 (20)

Binary indexed tree
Binary indexed treeBinary indexed tree
Binary indexed tree
 
マルチコアを用いた画像処理
マルチコアを用いた画像処理マルチコアを用いた画像処理
マルチコアを用いた画像処理
 
kagami_comput2015_8
kagami_comput2015_8kagami_comput2015_8
kagami_comput2015_8
 
Lecture2
Lecture2Lecture2
Lecture2
 
Indeedなう 予選A 解説
Indeedなう 予選A 解説Indeedなう 予選A 解説
Indeedなう 予選A 解説
 
C言語ポインタ講座 (Lecture of Pointer in C)
C言語ポインタ講座 (Lecture of Pointer in C)C言語ポインタ講座 (Lecture of Pointer in C)
C言語ポインタ講座 (Lecture of Pointer in C)
 
8_C言語入門 - 条件分岐について(if-else if-else)
8_C言語入門 - 条件分岐について(if-else if-else)8_C言語入門 - 条件分岐について(if-else if-else)
8_C言語入門 - 条件分岐について(if-else if-else)
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISる
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門
 
動的計画法の並列化
動的計画法の並列化動的計画法の並列化
動的計画法の並列化
 
第15回 配信講義 計算科学技術特論B(2022)
第15回 配信講義 計算科学技術特論B(2022)第15回 配信講義 計算科学技術特論B(2022)
第15回 配信講義 計算科学技術特論B(2022)
 
AtCoder Regular Contest 026 解説
AtCoder Regular Contest 026 解説AtCoder Regular Contest 026 解説
AtCoder Regular Contest 026 解説
 
El text.tokuron a(2019).watanabe190613
El text.tokuron a(2019).watanabe190613El text.tokuron a(2019).watanabe190613
El text.tokuron a(2019).watanabe190613
 
TeamLabLT20120630
TeamLabLT20120630TeamLabLT20120630
TeamLabLT20120630
 
kagamicomput201808
kagamicomput201808kagamicomput201808
kagamicomput201808
 
AtCoder Beginner Contest 010 解説
AtCoder Beginner Contest 010 解説AtCoder Beginner Contest 010 解説
AtCoder Beginner Contest 010 解説
 
AtCoder Beginner Contest 019 解説
AtCoder Beginner Contest 019 解説AtCoder Beginner Contest 019 解説
AtCoder Beginner Contest 019 解説
 
Pythonで始めた数値計算の授業@わんくま勉強会2018-04
Pythonで始めた数値計算の授業@わんくま勉強会2018-04Pythonで始めた数値計算の授業@わんくま勉強会2018-04
Pythonで始めた数値計算の授業@わんくま勉強会2018-04
 
超LINQ入門
超LINQ入門超LINQ入門
超LINQ入門
 
kagami_comput2016_08
kagami_comput2016_08kagami_comput2016_08
kagami_comput2016_08
 

【解説】JOI 2019/2020 一次予選 最速非公式解説【競技プログラミング】

  • 1. JOI 2019/2020 一次予選 第1回 最速非公式解説 きたむー (@Pro_ktmr)
  • 2. A – 3つの整数 Three Integers JOI 2019/2020 一次予選① 最速非公式解説
  • 3. A 問題概要 • 3 つの整数 𝐴, 𝐵, 𝐶 が与えられ る • 𝐴, 𝐵, 𝐶 はいずれも 1 または 2 である • 1 と 2 のうち,どちらが多くあ るか? JOI 2019/2020 一次予選① 最速非公式解説 問題文の公開は許可を頂いています
  • 4. A 考察① • if文を用いた場合分けを考える • 愚直に場合分けすると, 𝐴, 𝐵, 𝐶 = 1,1,1 , 1,1,2 , ⋯ , 2,2,2 と,8 通りも あって大変 • できなくはないがエレガントな解法ではない • 数が「1」または「2」であることをうまく利用できない か? JOI 2019/2020 一次予選① 最速非公式解説
  • 5. A 考察② • 𝐴, 𝐵, 𝐶 の和 𝐴 + 𝐵 + 𝐶 に着目してみる • 解説者は平均を求める際に和を取ることなどから発想 • 1 が 3 個,2 が 0 個 𝐴 + 𝐵 + 𝐶 = 3 • 1 が 2 個,2 が 1 個 𝐴 + 𝐵 + 𝐶 = 4 • 1 が 1 個,2 が 2 個 𝐴 + 𝐵 + 𝐶 = 5 • 1 が 0 個,2 が 3 個 𝐴 + 𝐵 + 𝐶 = 6 • よって, 𝐴 + 𝐵 + 𝐶 ≧ 5 であれば 2 が多く, 𝐴 + 𝐵 + 𝐶 < 5 であれば 1 の方が多い (問題の同値変形) JOI 2019/2020 一次予選① 最速非公式解説
  • 6. A 実装例 C++ #include"bits/stdc++.h" using namespace std; int main(){ int A, B, C; cin >> A >> B >> C; if(A+B+C >= 5){ cout << 2 << endl; } else{ cout << 1 << endl; } } Python3 a,b,c = map(int,input().split()) if a+b+c >= 5: print(2) else: print(1) JOI 2019/2020 一次予選① 最速非公式解説 変数の宣言 入力の受け取り 条件分岐と 答えの出力 入力の受け取り 条件分岐と 答えの出力
  • 7. B – 母音を数える Counting Vowels JOI 2019/2020 一次予選① 最速非公式解説
  • 8. B 問題概要 • 長さ 𝑁 の英小文字からなる文字 列 𝑆 が与えられる • 𝑆 のうち母音字(a,i,u,e,o)の個 数を求めよ JOI 2019/2020 一次予選① 最速非公式解説 問題文の公開は許可を頂いています
  • 9. B 考察 【文字列問題の常識】 • 文字列全体を一度に処理することは難しい • ループを回して 1 文字ずつ着目していく 【本問の考察】 • 母音を数えるためのカウンター変数を用意する • 着目している文字が母音ならカウンターを加算する JOI 2019/2020 一次予選① 最速非公式解説
  • 10. B 実装の前に 文字列の扱い方を確認しよう 【C++】 • 文字列はstring型,文字はchar型 • 文字列から 𝑖 文字目 0 ≦ 𝑖 < 𝑁 だけを取り出すに は 𝑆[𝑖] とする • 0 文字目から始まることに注意 • 文字は'a'のようにシングルクオーテーションで囲む Pythonは省略 JOI 2019/2020 一次予選① 最速非公式解説
  • 11. B 実装例 C++ #include"bits/stdc++.h" using namespace std; int main(){ int N; string S; cin >> N >> S; int counter = 0; for(int i=0; i<N; i++){ if(S[i] == 'a') counter++; if(S[i] == 'i') counter++; if(S[i] == 'u') counter++; if(S[i] == 'e') counter++; if(S[i] == 'o') counter++; } cout << counter << endl; } Python3 n = int(input()) s = input() counter = 0 for c in s: if c == 'a': counter += 1 if c == 'i': counter += 1 if c == 'u': counter += 1 if c == 'e': counter += 1 if c == 'o': counter += 1 print(counter) JOI 2019/2020 一次予選① 最速非公式解説 文字列はstring型 ループはfor文 文字は''で囲む ループはfor文 Pythonでは 文字と文字列の 区別がない
  • 12. C – マージ Merge JOI 2019/2020 一次予選① 最速非公式解説
  • 13. C 問題概要 • 共に広義単調増加な長さ 𝑁 の数列 𝐴 と長さ 𝑀 の数列 𝐵 が与えられる • 空の数列 𝐶 を用意する • 𝐴 と 𝐵 の先頭のうち小さい方を 𝐶 の末尾に追加し,その先頭の要素を 削除する • 𝐶 の要素数は最終的に 𝑁 + 𝑀 にな るが,そのときの中身を出力せよ JOI 2019/2020 一次予選① 最速非公式解説 問題文の公開は許可を頂いています
  • 14. C 実装例 考察 • 問題文に書かれている操 作をそのまま実装すれば よい • 数列は「配列」を用いて 実装する • 数列 𝐴, 𝐵 ともに,今の先 頭はどこかという情報を 変数 𝑖, 𝑗 で持つ C++ #include"bits/stdc++.h" using namespace std; int main(){ int N, M, A[500], B[500]; cin >> N >> M; for(int i=0; i<N; i++) cin >> A[i]; for(int j=0; j<M; j++) cin >> B[j]; int i = 0; int j = 0; for(int k=0; k<N+M; k++){ if(i == N){ cout << B[j] << endl; j++; } else if(j == M){ cout << A[i] << endl; i++; } else{ if(A[i] <= B[j]){ cout << A[i] << endl; i++; } else{ cout << B[j] << endl; j++; } } } } JOI 2019/2020 一次予選① 最速非公式解説 実装が大変だけど我慢しよう! ・・・本当に?
  • 15. C 考察 • ちょっと待った!!! • 前ページは正しいが,もっとエレガントな解法がある • 出力例を見ると,広義単調増加になっている • よくよく考えると,小さい方を選んでいるから当たり前 • 実はこのアルゴリズムは「マージソート」というソート アルゴリズムの一部である • 問題名「マージ」もここからきている • よって,数列 𝐴, 𝐵 をごちゃまぜにして,ソートしたも のを出力すればよい JOI 2019/2020 一次予選① 最速非公式解説 少し立ち止まって,問題の意味を 考えてみることも大切!
  • 16. C 実装例 C++ #include"bits/stdc++.h" using namespace std; int main(){ int N, M, C[1000]; cin >> N >> M; for(int i=0; i<N+M; i++){ cin >> C[i]; } sort(C, C+N+M); for(int i=0; i<N+M; i++){ cout << C[i] << endl; } } Python3 n, m = map(int,input().split()) c = list(map(int,input().split())) c += list(map(int,input().split())) c.sort() for c_i in c: print(c_i) JOI 2019/2020 一次予選① 最速非公式解説 最初から要素数 𝑁 + 𝑀 の数列 𝐶 だけを考える ソート(並び替え)は 書き方に注意 最初から要素数 𝑁 + 𝑀 の数列 𝐶 だけを考える 【Tips】 マージソートは再帰的な処理や分割統治の仕組みに ついて理解するのに良い例だ 今回の3問が解けた人は是非挑戦してみよう! ▼おすすめリンク • https://www.codereading.com/algo_an d_ds/algo/merge_sort.html • https://onlinejudge.u- aizu.ac.jp/problems/ALDS1_5_B
  • 17. JOI 2019/2020 一次予選 第2回 最速非公式解説 きたむー (@Pro_ktmr)
  • 18. A – 試験 Exam JOI 2019/2020 一次予選② 最速非公式解説
  • 19. A 問題概要 • 3 つの整数 𝐴, 𝐵, 𝐶 が与えられる • 𝐴, 𝐵, 𝐶 のうち高い方から 2 つを足 し合わせた合計は? JOI 2019/2020 一次予選② 最速非公式解説
  • 20. A 考察① • まずは if 文を用いた場合分けを考える • 愚直に場合分けすると, 𝐴 ≧ 𝐵 ≧ 𝐶, 𝐴 ≧ 𝐵 ≧ 𝐶, 𝐵 ≧ 𝐴 ≧ 𝐶, 𝐵 ≧ 𝐶 ≧ 𝐴, 𝐶 ≧ 𝐴 ≧ 𝐵, 𝐶 ≧ 𝐵 ≧ 𝐴 と,6 通りもあって大変 • できなくはないがエレガントな解法ではない • 求める値「大きい方から 2 つの合計」をうまく言い換 えられないか? JOI 2019/2020 一次予選② 最速非公式解説
  • 21. A 考察② • 「大きい方から 2 つの合計」は「3 つの合計から最小 値を引いたもの」と言い換えられる (問題の同値変形) • この言い換えは既に出題されている 「科目選択」(2015/2016 予選 A)など • 最小値は場合分けではなく,「仮定して更新」という 考え方を用いて求められる 1. 最小値を 𝑚 とおき,𝐴 が最小と仮定して 𝑚 = 𝐴 とする 2. もし 𝑚 > 𝐵 なら最小値を 𝑚 = 𝐵 で更新する 3. もし 𝑚 > 𝐶 なら最小値を 𝑚 = 𝐶 で更新する JOI 2019/2020 一次予選② 最速非公式解説
  • 22. A 実装例 C++ #include"bits/stdc++.h" using namespace std; int main(){ int A, B, C; cin >> A >> B >> C; int m = A; if(m > B){ m = B; } if(m > C){ m = C; } cout << A + B + C - m << endl; } Python3 a,b,c = map(int,input().split()) m = a if m > b: m = b if m > c: m = c print(a + b + c - m) JOI 2019/2020 一次予選② 最速非公式解説 変数の宣言 入力の受け取り 最小値の計算 入力の受け取り 最小値の計算 【Tips】 最小値は様々な方法で求めることが出来る ・ここで紹介した方法は要素数が大きくなっても対 応しやすい方法だ ・他にも C++ であれば min 関数を用いて int m = min({A, B, C}); などとすることもできる
  • 23. B – 文字列の反転 Inversion of a String JOI 2019/2020 一次予選② 最速非公式解説
  • 24. B 問題概要 • 長さ 𝑁 の文字列 𝑆 が与えられる • 𝑆 の 𝐴 文字目から 𝐵 文字目まで の並びを逆にした文字列は? • ただし,𝑆 の先頭は 1 文字目で 末尾が 𝑁 文字目 JOI 2019/2020 一次予選② 最速非公式解説 J O I j o i J o I j J O J i o j I o I j
  • 25. B 考察の前に 文字列の扱い方を確認しよう 【C++】 • 文字列は string 型 • 文字列から 𝑖 文字目 0 ≦ 𝑖 < 𝑁 だけを取り出すに は 𝑆[𝑖] とする • 𝟎 文字目から始まることに注意 Python は省略 JOI 2019/2020 一次予選② 最速非公式解説 0 1 2 3 4 5 6 7 8 9 J O I j o i J o I j ▲ 𝐴 − 1 文字目 ▲ 𝐵 − 1 文字目
  • 26. B 考察 【文字列問題の常識】 • 文字列全体を一度に処理することは難しい • ループを回して 1 文字ずつ着目していく 【本問の考察】 • 出来上がった文字列は「もとのまま」「反転している」 「もとのまま」と 3 つの部分からなっている • 「反転している」の部分はループを逆向きに回す • つまり 0 → 𝐴 − 2, 𝐵 − 1 → 𝐴 − 1, 𝐵 → 𝑁 − 1 JOI 2019/2020 一次予選② 最速非公式解説
  • 27. B 実装例 C++ #include"bits/stdc++.h" using namespace std; int main(){ int N, A ,B; string S; cin >> N >> A >> B >> S; for(int i=0; i<=A-2; i++){ cout << S[i]; } for(int i=B-1; i>=A-1; i--){ cout << S[i]; } for(int i=B; i<=N-1; i++){ cout << S[i]; } cout << endl; } Python3 n,a,b = map(int,input().split()) s = input() i = 0 while i<=a-2: print(s[i], end="") i += 1 i = b-1 while i >=a-1: print(s[i], end="") i -= 1 i = b while i <= n-1: print(s[i], end="") i += 1 JOI 2019/2020 一次予選② 最速非公式解説 文字列はstring型 ループはfor文 逆向きにループを 回すときは不等号 の向きとi--に注意 複雑なループは while文 改行無しで出力す るときはend="" 【Tips】 reverse関数を用いれば簡単に文字列を反転させ ることが出来る (C++の場合は下) reverse(S.begin()+A-1, S.begin()+B); for文で逆向きの ループを回す方法 も調べてみよう!
  • 28. C – 最頻値 Mode JOI 2019/2020 一次予選② 最速非公式解説
  • 29. C 問題概要 • 長さ 𝑁 の数列 𝐴 が与えられる • 1 ≦ 𝐴𝑖 ≦ 𝑀 ≦ 100 • 長さ 𝑀 の数列 𝐵 を次のように定 義する: 𝐵𝑗 の値は 𝐴𝑖 = 𝑗 である 𝑖 の個数 • 数列 𝐵 の最大値は? JOI 2019/2020 一次予選② 最速非公式解説
  • 30. C 実装例 考察 • 問題文に書かれている定 義をそのまま実装すれば よい • 数列は「配列」を用いて 実装する • 最大値 𝑀 は問題 A で紹 介した方法で求められる • 数列と配列のずれに注意 C++ #include"bits/stdc++.h" using namespace std; int main(){ int N, M, A[100], B[101]; cin >> N >> M; for(int i=0; i<N; i++) cin >> A[i]; for(int j=1; j<=M; j++){ B[j] = 0; for(int i=0; i<N; i++){ if(A[i] == j) B[j]++; } } int m = 0; for(int j=1; j<=M; j++){ if(m > B[j]) m = B[j]; } cout << m << endl; } JOI 2019/2020 一次予選② 最速非公式解説 実装が大変だけど,前回より ましだから我慢しよう! ・・・本当に?
  • 31. C 考察 • ちょっと待った!!! • 前ページは正しいが,数列 𝐵 はもっとエレガントな方 法で求められる • 𝐵𝑗 は数列 𝐴 中の 𝑗 の登場回数だから,𝐴 の視点から 見て,𝐵𝐴 𝑖 に 1 ずつ加算していけばよい • 意味不明だと思うので実装例要参照 • 数列 𝐵 を先に 0 で初期化しておくことを忘れずに JOI 2019/2020 一次予選② 最速非公式解説
  • 32. C 実装例 C++ #include"bits/stdc++.h" using namespace std; int main(){ int N, M, B[101]; cin >> N >> M; for(int j=1; j<=M; j++) B[j] = 0; for(int i=0; i<N; i++){ int A_i; cin >> A_i; B[A_i]++; } int m = 0; for(int j=1; j<=M; j++){ if(m < B[j]) m = B[j]; } cout << m << endl; } Python3 n,m = map(int,input().split()) a = list(map(int,input().split())) b = [0] * 101 for a_i in a: b[a_i] += 1 answer = 0 for b_j in b: if answer < b_j: answer = b_j print(answer) JOI 2019/2020 一次予選② 最速非公式解説 数列 𝐵 を 0 で 初期化しておく 𝐵 𝐴 𝑖 に 1 加算 最大値は問題 A の 方法で求める 数列 𝐵 を 0 で 初期化しておく 𝐵 𝐴 𝑖 に 1 加算 最大値は問題 A の 方法で求める 【Tips】 最大値,最小値はsort関数を用いて求めることも出 来るが,計算に少し時間がかかる Pythonの場合はmax関数,min関数が使える
  • 33. JOI 2019/2020 一次予選 第3回 最速非公式解説 きたむー (@Pro_ktmr)
  • 34. A – Xに最も近い値 The Nearest Value JOI 2019/2020 一次予選③ 最速非公式解説
  • 35. A 問題概要 • 3 つの整数 𝑋, 𝐿, 𝑅 が与えら れる • 𝐿 以上 𝑅 以下の整数のうち, 𝑋 との差の絶対値が最も小さ い値は? JOI 2019/2020 一次予選③ 最速非公式解説
  • 36. A 考察① • 「差の絶対値が小さい」とは何か • これは数直線上の 2 数の距離を表すと考えられる • つまり,𝑋 と数直線上の距離が最も近い数 𝑎 (𝐿 ≦ 𝑎 ≦ 𝑅) が答え(問題の同値変形) JOI 2019/2020 一次予選③ 最速非公式解説 𝑋 𝑎 |𝑋 − 𝑎|
  • 37. A 考察② • 𝑋 と区間 𝐿~𝑅 の位置関係で場合分けする • これを if 文を用いて実装すればよい JOI 2019/2020 一次予選③ 最速非公式解説 (i) 𝑿 が区間の左側 • 数式で表すと, 𝑋 < 𝐿 のとき • 図より,答えは 𝐿 (ii) 𝑿 が区間内 • 数式で表すと, 𝐿 ≦ 𝑋 ≦ 𝑅 のとき • 図より,答えは 𝑋 (iii) 𝑿 が区間の右側 • 数式で表すと, 𝑅 < 𝑋 のとき • 図より,答えは 𝑅 𝑋 𝐿 𝑅 𝑎 𝐿 𝑋 𝑅 𝑎 𝐿 𝑅 𝑋 𝑎
  • 38. A 実装例 C++ #include"bits/stdc++.h" using namespace std; int main(){ int X, L, R; cin >> X >> L >> R; if(X < L){ cout << L << endl; } if(L <= X && X <= R){ cout << X << endl; } if(R < X){ cout << R << endl; } } Python3 x,l,r = map(int,input().split()) if x < l: print(l) if l <= x <= r: print(x) if r < x: print(r) JOI 2019/2020 一次予選③ 最速非公式解説 変数の宣言 入力の受け取り 場合分けの実装 入力の受け取り 場合分けの実装 条件式の「かつ」は && L <= X <= R とは できないので注意!
  • 39. B – キャピタリゼーション Capitalization JOI 2019/2020 一次予選③ 最速非公式解説
  • 40. B 問題概要 • 長さ 𝑁 の文字列 𝑆 が与えられる • 𝑆 はすべて英小文字である • 𝑆 に含まれる連続する文字列 'joi' をすべて 'JOI' に置き換 えた文字列は? JOI 2019/2020 一次予選③ 最速非公式解説 j o i n o j o i j i n J O I n o J O I j i n
  • 41. B 考察の前に 文字列の扱い方を確認しよう 【C++】 • 文字列は string 型,文字は char 型 • 文字は'a'のようにシングルクオーテーションで囲む • 文字列から 𝑖 文字目 0 ≦ 𝑖 < 𝑁 だけを取り出すに は 𝑆[𝑖] とする • 𝟎 文字目から始まることに注意 JOI 2019/2020 一次予選③ 最速非公式解説 0 1 2 3 4 5 6 7 8 9 10 j o i n o j o i j i n
  • 42. B 考察 【文字列問題の常識】 • 文字列全体を一度に処理することは難しい • ループを回して 1 文字ずつ着目していく 【本問の考察】 • 今見ている文字を 𝑆[𝑖] として, 𝑆 𝑖 = 'j' かつ 𝑆 𝑖 + 1 = 'o' かつ 𝑆 𝑖 + 2 = 'i' であれば, それぞれ 'J' 'O' 'I' に置き換える • ループを 0~𝑁 − 1 で回すとはみ出すので注意 JOI 2019/2020 一次予選③ 最速非公式解説 N-3 N-2 N-1 … o j o ない 𝑖 = 𝑁 − 2 のとき
  • 43. B 実装例 C++ #include"bits/stdc++.h" using namespace std; int main(){ int N; string S; cin >> N >> S; for(int i=0; i+2<N; i++){ if(S[i] == 'j' && S[i+1] == 'o' && S[i+2] == 'i'){ S[i] = 'J'; S[i+1] = 'O'; S[i+2] = 'I'; } } cout << S << endl; } Python3 n = int(input()) s = input() print(s.replace('joi', 'JOI')) JOI 2019/2020 一次予選③ 最速非公式解説 文字列はstring型 ループはfor文 𝑆[𝑖 + 2] が 𝑁 文字 に含まれるよう, 条件式を設定 replace 関数を 用いた 【Tips】 Python における関数の利用 Python には C++ と比べて様々な関数が用意さ れており,それらを用いれば簡単に問題が解けるこ ともある ただし,高みを目指すのであれば,その関数の中で どのような処理が行われているのか,しっかり把握 しておこう
  • 44. C – 最長昇順連続部分列 Longest Ascending Contiguous Subsequence JOI 2019/2020 一次予選③ 最速非公式解説
  • 45. C 問題概要 • 長さ 𝑁 の数列 𝐴 が与えられる • 1 ≦ 𝑁 ≦ 100 • 数列 𝐴 の連続部分列の中で昇 順に並んでいるもののうち最長 のものの長さは? • すなわち,𝐴𝑙 ≦ 𝐴𝑙+1 ≦ ⋯ ≦ 𝐴 𝑟 を満たすような 2 整数 𝑙, 𝑟 につ いて, 𝑟 − 𝑙 + 1 の最大値は? JOI 2019/2020 一次予選③ 最速非公式解説
  • 46. C 実装例 考察 • 問題文にわざわざ数式 や文字 l, r を用いて条 件が書かれているから, そのまま実装する • 数列は「配列」を用いて 実装する • 最大値 𝑎𝑛𝑠 は前回紹介し た方法で求められる C++ #include"bits/stdc++.h" using namespace std; int main(){ int N, A[100]; cin >> N; for(int i=0; i<N; i++) cin >> A[i]; int ans = 0; for(int l=0; l<N; l++){ for(int r=l; r<N; r++){ int ok = 1; for(int i=l+1; i<=r; i++){ if(A[i-1] > A[i]) ok = 0; } if(ok == 1 && ans < r-l+1) ans = r-l+1; } } cout << ans << endl; } JOI 2019/2020 一次予選③ 最速非公式解説 実装が大変だけど,我慢しよう! ・・・本当に?
  • 47. C 考察 • ちょっと待った!!! • 前ページは正しいが,もっとエレガントな解法がある • 前ページはすべての組 (𝑙, 𝑟) について,昇順か確か めているが,その必要はない • 任意の 1 要素は昇順だから答えは絶対に 1 以上 • 最初は 𝑟 = 𝑙 + 1 として,𝑟 を 1 ずつ大きくしていき, その都度昇順が維持されているか確認すればよい • 昇順が維持されなくなったらその 𝑙 はもう無理 JOI 2019/2020 一次予選③ 最速非公式解説
  • 48. C 実装例 C++ #include"bits/stdc++.h" using namespace std; int main(){ int N, A[100]; cin >> N; for(int i=0; i<N; i++) cin >> A[i]; int ans = 1; for(int l=0; l<N; l++){ for(int r=l+1; r<N; r++){ if(A[r-1] > A[r]) break; if(ans < r-l+1) ans = r-l+1; } } cout << ans << endl; } Python3 n = int(input()) a = list(map(int,input().split())) ans = 1 for l in range(n): for r in range(n): if l >= r: continue if a[r-1] > a[r]: break if ans < r-l+1: ans = r-l+1 print(ans) JOI 2019/2020 一次予選③ 最速非公式解説 昇順が維持されなく なったらループ終了 break; を使う 最大値は前回紹介し た方法で求める 最大値は前回紹介し た方法で求める 昇順が維持されなく なったらループ終了 break を使う 𝑟 = 𝑙 + 1 となるまで continue を使って待つ
  • 49. C 別解考察 • 与えられた数列の隙間に,その 2 数の大小関係 (≦, >) を書いてみる (下は入力例 3) • このとき,当然 ≦ が連続して並ぶ個数の最大値+1 が答え • 前から,今 ≦ が何回続いているか更新しつつ処理 JOI 2019/2020 一次予選③ 最速非公式解説 1 2 2 12 120 210 202 1010 2020 ≦ ≦ ≦ ≦ ≦ > ≦ ≦
  • 50. C 別解実装例 C++ #include"bits/stdc++.h" using namespace std; int main(){ int N, A[100]; cin >> N; for(int i=0; i<N; i++) cin >> A[i]; int ans = 0; int now = 0; for(int i=0; i+1<N; i++){ if(A[i] <= A[i+1]) now++; else now = 0; ans = max(ans, now); } cout << ans+1 << endl; } Python3 n = int(input()) a = list(map(int,input().split())) ans = 0 now = 0 for i in range(n-1): if a[i] <= a[i+1]: now += 1 else: now = 0 ans = max(ans, now) print(ans+1) JOI 2019/2020 一次予選③ 最速非公式解説 最大値はmax関数を 使っても求められる 今 ≦ が何回続いて いるか管理する 最大値はmax関数を 使っても求められる 今 ≦ が何回続いて いるか管理する 【Tips】 ループの回数 この問題では計 3 つの解法を紹介したが,最初の 解法は 3 重ループ,標準解法は 2 重ループ,別解 は 1 重ループを用いた 各ループの回る回数が同じとき,〇重の〇が小さい ほど処理が高速で,性能の良いプログラムとなる 今回の問題が解けた人は,「時間計算量」についてよ り深く学んでみよう!