AtCoder Beginner Contest 194(Rated範囲外)
- A - I Scream
- B - Job Assignment
- C - Squared Error
- D - Journey
- E - Mex Min
- F - Digits Paradise in Hexadecimal
コンテスト前のレート:2029
(Ratedだとしたら)レート通りのパフォーマンスが得られる順位:318位(1500点、31分51秒)
今回もまた一瞬適当にD問題辺りからやろうとするが、結局素直にA問題から。
A - I Scream
問題
すごい誤読しやすそうな見た目をしていて、Unratedなら気楽でよかったと思った。
思考過程
- 例1の説明を見て、とりあえずを計算しておく。
- あとは問題文の通り上の条件から順に判定していく。
import java.util.Scanner; public class Main { public static void main(String[] args) throws Exception { Scanner sc = new Scanner(System.in); int a = sc.nextInt(); int b = sc.nextInt(); sc.close(); int c = a + b; if (c >= 15 && b >= 8) { System.out.println(1); } else if (c >= 10 && b >= 3) { System.out.println(2); } else if (c >= 3) { System.out.println(3); } else { System.out.println(4); } } }
ここまで2分44秒+0ペナ。510位。
B - Job Assignment
問題
でよいことに全く気付いておらず、手間取りすぎ。
思考過程
- とについて上位人まで見ることがあり、位を管理するのは面倒なので、PriorityQueueを用意しておく。
- とが同じ人かわからないと困るので、情報はペア(オブジェクト)で持っておく。
- と(以降、添え字は何番目に小さいかを表すものとする)が別人の場合は、との大きい方が答え。
- とが同じ人の場合は、以下の内最小のものが答え。
- との大きい方
- との大きい方
import java.util.PriorityQueue; import java.util.Scanner; public class Main { public static void main(String[] args) throws Exception { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); PriorityQueue<Obj> q1 = new PriorityQueue<>((o1, o2) -> o1.a - o2.a); PriorityQueue<Obj> q2 = new PriorityQueue<>((o1, o2) -> o1.b - o2.b); for (int i = 0; i < n; i++) { Obj o = new Obj(); o.a = sc.nextInt(); o.b = sc.nextInt(); q1.add(o); q2.add(o); } sc.close(); Obj o1 = q1.poll(); Obj o2 = q2.poll(); int ans = Math.max(o1.a, o2.b); if (o1 == o2) { Obj o12 = q1.poll(); Obj o22 = q2.poll(); ans = Math.min(Math.max(o1.a, o22.b), Math.max(o12.a, o2.b)); ans = Math.min(ans, o1.a + o2.b); } System.out.println(ans); } static class Obj { int a, b; } }
ここまで12分19秒+0ペナ。1775位。
C - Squared Error
問題
苦手意識のある、通り全探索したいけどでは間に合わない系統の問題。
思考過程
- ソートをしておけば、各に対するをまとめて回で計算するようなことができるのではないかと思ってとりあえずソートしてみる(が意味なかった)。
- 問題文の式について、の中身を展開してみると、となる。
- との部分については、二次元表を書いて数えてみると、~が全体で回ずつ足されることがわかる。
- の部分は、の和を保持しながら計算することで、にできる。
import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Arrays; public class Main { public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine()); String[] sa = br.readLine().split(" "); int[] a = new int[n]; for (int i = 0; i < n; i++) { a[i] = Integer.parseInt(sa[i]); } br.close(); Arrays.sort(a); // 1 // 3 long ans = 0; for (int i = 0; i < n; i++) { ans += a[i] * a[i]; } ans *= n - 1; // 4 long sum = 0; for (int i = n - 2; i >= 0; i--) { sum += a[i + 1]; ans -= 2 * a[i] * sum; } System.out.println(ans); } }
ここまで22分43秒+0ペナ。1369位。
D - Journey
問題
確率という単語が目に入っただけで後回しにせず、そのまま最初に解けば、最大瞬間風速4位にはなれていそうだった。
思考過程
- 未訪問の頂点が選ばれるまでの回数の期待値を求める。ということを回行う。
- 未訪問頂点が選ばれる確率は、「未訪問頂点の数」であり、期待値は確率の逆数。
import java.util.Scanner; public class Main { public static void main(String[] args) throws Exception { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); sc.close(); double ans = 0; for (int i = n - 1; i > 0; i--) { ans += (double) n / i; } System.out.println(ans); } }
ここまで25分16秒+0ペナ。529位。
E - Mex Min
問題
ここ2ヶ月青diff以下を1問も落としていなかったのに、まさかの緑diffを落とす。
残り時間全部E問題に使ったわけではなく、F問題で逆転を狙うために30分くらいは残して諦めたのもあるけど。
余計なことをしてバグらせていたのと、WAやREを出していたコーナーケースが何なのか見つけ出せなかった。(答えがになる場合だった。)
思考過程
- 出現数を持っておく配列と「現在の出現数の最小値:val」を用意し、これを管理しながら進めることでまたはで解こうとする。
- 最初はから順に調べていき、最初の出現数の値をvalとする。
- 配列を更新していく間に、出現数のものが新たに出てきたり消えたりした時にするため、新しいvalを高速に求めるためには、前後の出現数の位置も管理しておく必要があるのではと思った。
- 前後の出現数の位置を管理するために、出現数の値の集合を持つTreeSetも用意する。
- コンテスト内最後の提出がこれだが、どうやら行目でvalがnになっていて配列外参照していたらしい。(コンテスト中は、RE発生箇所は行目よりは後だろうと疑っていなかった。)
- コンテスト後になって、新しいvalはSet内の最小値を取ればいいだけであることがわかり、別に必要なかった前後の出現数の管理部分を丸ごと削除する。
- それでもまだREが取れず、テストケース名を見てやっと答えがになる場合が怪しいことがわかり、その対処をする。
import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.TreeSet; public class Main { public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] sa = br.readLine().split(" "); int n = Integer.parseInt(sa[0]); int m = Integer.parseInt(sa[1]); sa = br.readLine().split(" "); int[] a = new int[n]; for (int i = 0; i < n; i++) { a[i] = Integer.parseInt(sa[i]); } br.close(); // 1 int[] cnt = new int[n]; for (int i = 0; i < m; i++) { cnt[a[i]]++; } // 4 TreeSet<Integer> set = new TreeSet<>(); set.add(n); // 7 for (int i = 0; i < n; i++) { if (cnt[i] == 0) { set.add(i); } } int ans = set.first(); for (int i = m; i < n; i++) { int x = a[i]; if (cnt[x] == 0) { set.remove(x); } cnt[x]++; int y = a[i - m]; if (cnt[y] == 1) { set.add(y); ans = Math.min(ans, y); } cnt[y]--; } System.out.println(ans); } }
F - Digits Paradise in Hexadecimal
問題
ある程度惜しいところまではできていたと思うが、時間内には解き切れず。
詳細は省略するが、先頭0周りの処理が色々おかしかった。
下記6.でfor文2つに分けたことにより'9'が漏れたりとかも。
結局EとF両方手を出して両方ともあと少し(と言えるかは若干怪しいが)できなかったという感じ。
思考過程
- 「文字目まで見て種類の場合の通り数」で桁DPすることを基本方針として考える。
- 種類のところを、これまでに使った文字の集合にしないと遷移できないのでは?と思うが、それだと遷移を抜きにしてもかかるので無理。
- 遷移の仕方を詰めていくと、桁DPのより小さいことが確定済みの方は、次の値として'0'~'F'の種類全部取れるので、その内種類数が増えない遷移が通り、種類数が増える遷移が通りとなる。
- ただし、種類数が→になる遷移は、最初の文字目の時は確定済みになっていないのでなし。文字目以降では'1'~'F'の通り。
- 桁DPのより小さいことが未確定の方は常に通りで、これまでに登場した文字のSetだけ持っておけば、Setのsizeを元に確定済みの方へと遷移できそう。
- (以降、の文字目をとする。)未確定から確定済みへの遷移は、の各について、Setに含まれていればへ、含まれていなければへの遷移を通りずつ加算する。
- ただし、最初の文字目だけは、文字種の状態から'0'は選べないので、'0'の分は加えない。
- 最後に、未確定状態を表すSetのsizeがちょうどならば通りを加算する。
import java.util.HashSet; import java.util.Scanner; import java.util.Set; public class Main { public static void main(String[] args) throws Exception { Scanner sc = new Scanner(System.in); char[] s = sc.next().toCharArray(); int k = sc.nextInt(); sc.close(); int n = s.length; int mod = 1000000007; Set<Character> set = new HashSet<>(); long[][] dp = new long[n + 1][17]; for (int i = 0; i < n; i++) { // 6 char end = (char) Math.min('9' + 1, s[i]); for (char c = '0'; c < end; c++) { // 7 if (c == '0' && i == 0) { continue; } if (set.contains(c)) { dp[i + 1][set.size()]++; } else { dp[i + 1][set.size() + 1]++; } } end = (char) Math.min('F', s[i]); for (char c = 'A'; c < end; c++) { if (set.contains(c)) { dp[i + 1][set.size()]++; } else { dp[i + 1][set.size() + 1]++; } } set.add(s[i]); // 4 if (i > 0) { dp[i + 1][1] += 15; } for (int j = 1; j <= k; j++) { // 3 dp[i + 1][j] += dp[i][j] * j; dp[i + 1][j] += dp[i][j - 1] * (16 - (j - 1)); dp[i + 1][j] %= mod; } } long ans = dp[n][k]; // 8 if (set.size() == k) { ans++; ans %= mod; } System.out.println(ans); } }
最終結果:ABCDの4完、25分16秒、1987位、パフォーマンス1102相当
レート変動:2029(Unrated)
こんな結果になるくらいなら、明日のAGC052で青落ちしておくべきだと思う。
1692以下くらいで落ちる見込み。(1704以下かも。端数処理がどうなっているのかわかってない。)