AtCoder Beginner Contest 114
AtCoder Beginner Contest 114 (ABC114) の解説です。
AtCoder Beginner Contest 114 旧版
AtCoder Beginner Contest 114 beta版
解けた問題だけ解説をしていきますー
要項
AtCoder Beginner Contest 114
rated 1200
12/2(日) 21:00 ~ 22:40 (100分)
100-200-300-400 (4問)
ペナルティ: 5分
方針
全完
A - 753
実際にやるだけです。
#include<iostream> using namespace std; int main() { int X; cin >> X; cout << (X == 3 || X == 5 || X == 7 ? "YES" : "NO"); return 0; }
B - 754
これも、先頭から順に取り出して比較するだけです。
なお、部分的な数字を取り出すときは、最初に文字列にしておいて後で数に変換すると楽です。
#include<iostream> #include<string> #include<algorithm> using namespace std; const int SUB = 6081; // '0' * 111 + 753 int main() { string S; cin >> S; int diff = 999; // 大きい数字、INFとかで良い for (int i = 2, l = S.size();i < l;++ i) diff = min(diff, abs(S[i - 2] * 100 + S[i - 1] * 10 + S[i] - SUB)); cout << diff; return 0; }
C - 755
先頭から桁を決め打ちしていくと、一種の動的計画法で解くことができます。
まず、簡単のためにをの形であるものとします。
を上から桁まで決め打った時、現れる数字の集合がである組み合わせの個数とします。
例えば、のように表すことができますね。
に関してはをと関連付けることでbitDPのようにして扱うことができます。
つまり、例えばを代わりにとして、とするわけですね。
これを用いて、上から順に遷移を解き、全てのを合計すれば答えになります。
次に、ちゃんとが任意の場合について考えられるようにしましょう。
これは、dpの状態にもう一個持たせるようにして、は上から桁まで決め打った時、現れる数字の集合がでかつ今までの状態がの上位桁と完全一致している場合を考えます。
これを用いて、このdp2はを超えない遷移しか認めないようにし、またより小さくなるならその分の値はの方に遷移させます(上位の桁から決めてるので、この時点でより小さいなら以降は全てより小さいため)
これにより、で求めることができます。
#include<iostream> #include<cstring> using namespace std; int main() { string N; cin >> N; int dp[10][8]; // 第x桁目に部分集合y(y:{3,5,7})ができている組み合わせ memset(dp, 0, sizeof(dp)); // 全ての初期値は0よね bool isCover = true; // 全てNと同じ値を選ぶような選び方が存在するか否か for (int i = 0, nowDigit, j, k, cover = 0;i < N.size();++ i) { nowDigit = N[i] & 15; // 今見ている桁の値 dp[i][0] = 1; // 第x桁目が空集合……すなわち今まで0だった、これが最初の桁の組み合わせは当然だが1通り for (j = 1;j < 8;j <<= 1) { for (k = 0;k < 8;++ k) dp[i + 1][j | k] += dp[i][k]; if (isCover && (j & 6) + 3 > nowDigit) -- dp[i + 1][j | cover]; // Nを超える場合をちょっと引き算 } cover |= 1 << (nowDigit - 3 >> 1); // 現時点で、Nと同じ値を連続して選んだ時の{3,5,7}の集合 isCover &= ((nowDigit | nowDigit << 1) & 7) == 7; // これA問題と同じだね、誘導問だったか } cout << dp[N.size()][7]; // {3,5,7}全てを含む場合のみが答えなので return 0; }
D - 756
まず、約数の個数の関係について知っておきましょう。
ある値が素因数分解の結果という形で表される時、その値の約数はという形になります。
例えば、なので、約数の個数は個となります。
さて、をまずは素因数分解していきましょう。
ここで、より小さい素数は25個しかないので、についてそれぞれ25個の素数で割って個数を数えていけば大丈夫ですね。
さて次に、約数の個数が個とのことなので、この条件を満たすか調べていきます。
これはまぁ、3重ループでも回しつつ素因数の個数が条件を満たすか調べていけば良いでしょう。
計算量は素因数分解のところで、数え上げでです……あってるこれ?
ちなみにこのコードでは面倒なので予め出しておいた素数で殴ってます、まぁわざわざ篩で高速化してもねぇ……。
#include<iostream> #include<cstring> using namespace std; int main() { int N; cin >> N; int divisor[25], prime[25] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}; // 約数の素因数分解結果 memset(divisor, 0, sizeof(divisor)); for (int i = 2, j, tmp;i <= N;++ i) for (j = 0, tmp = i;tmp > 1;++ j) for (int p = prime[j];tmp % p == 0;tmp /= p) ++ divisor[j]; // 素因数分解していくよー int ans = 0; for (int i = 0;i < 25;++ i) { if (divisor[i] >= 74) ++ ans; // a^74の形 for (int j = 0;j < 25;++ j) { if (i == j) continue; if (divisor[i] >= 4 && divisor[j] >= 14) ++ ans; // a^4*b^14の形 if (divisor[i] >= 2 && divisor[j] >= 24) ++ ans; // a^2*b^24の形 for (int k = j + 1;k < 25;++ k) if (i != k && divisor[i] >= 2 && divisor[j] >= 4 && divisor[k] >= 4) ++ ans; // a^2*b^4*c^4の形 } } cout << ans; return 0; }
結果
oooo 4完(1000点) 01:51-05:53-61:06-75:37 / 75:37
222位
感想
今回は知識と実装がともにやや難しめで、発想力を試されたのではないでしょうか。
ですが、恐らく枝刈りなどの手法で重い計算量でも強引にACできる可能性も高く、案外すんなり解けた人もいるのかもしれません。
まぁ何にせよ今回の問題は全て典型的な問題なので、競プロをやっていこうと思った場合はこの問題セットは確実に正解できるようになっておきたいですね。