はじめに
AtCoder Beginner Contest 234の復習記事です。
記事におけるScanner
クラスは、自作の入力クラスです。
コンテスト
https://atcoder.jp/contests/abc234
問題A
F(x)
を定義し、求める式をG(x)
とした時に、G(T)
の値が答えとなります。
public static void Solve()
{
var T = Scanner.Scan<int>();
long F(long x) => x * x + 2 * x + 3;
long G(long x) => F(F(F(x) + x) + F(F(x)));
var answer = G(T);
Console.WriteLine(answer);
}
問題B
すべての点の組み合わせを試すことで求めることができます。
最後にSqrt
を取ることで誤差を回避しました。
public static void Solve()
{
var N = Scanner.Scan<int>();
var P = new (double X, double Y)[N];
for (var i = 0; i < N; i++)
{
var (x, y) = Scanner.Scan<double, double>();
P[i] = (x, y);
}
var answer = 0d;
foreach (var (x1, y1) in P)
{
foreach (var (x2, y2) in P)
{
var (dx, dy) = (x2 - x1, y2 - y1);
answer = Math.Max(answer, dx * dx + dy * dy);
}
}
answer = Math.Sqrt(answer);
Console.WriteLine(answer);
}
問題C
K
を2進数表記したとき、1
の値を2
にしたものが答えとなります。
public static void Solve()
{
var K = Scanner.Scan<long>();
var list = new List<long>();
while (K > 0)
{
list.Add(K & 1);
K >>= 1;
}
list.Reverse();
var answer = string.Join("", list.Select(x => x * 2));
Console.WriteLine(answer);
}
問題D
優先度付きキューを使い、キューのサイズがK
個の時の最小の値がそれぞれの状態の答えとなります。
最初は、先頭からK
個の値を入れたときの最小の値を表示し、それ以降は追加される値がキューの最小よりも小さい場合はK番目より小さいので無視し、大きい場合はキューを更新することでK
個を維持しつつ最小の値を管理できます。
public static void Solve()
{
var (N, K) = Scanner.Scan<int, int>();
var P = Scanner.ScanEnumerable<int>().ToArray();
var queue = new PriorityQueue<int>();
for (var i = 0; i < K; i++)
{
queue.Enqueue(P[i]);
}
Console.WriteLine(queue.Peek());
for (var i = K; i < N; i++)
{
if (queue.Peek() < P[i])
{
queue.Dequeue();
queue.Enqueue(P[i]);
}
var answer = queue.Peek();
Console.WriteLine(answer);
}
}
問題E
答えとしてあり得る値を考えたとき、
- 等差が
-9
から9
(19通り) - 最上位の桁が
0
から9
(10通り) - 桁数が17桁 (17通り)
すべて合わせて3230(19*10*17)
通りのうち、各桁の値が0
から9
の条件をもつ値すべてを全探索しすることで、X
以上を最小の値を求めることができる。
public static void Solve()
{
var X = Scanner.Scan<long>();
var hashset = new HashSet<long>(Enumerable.Range(0, 10).Select(x => (long)x));
for (var d = -9; d <= 9; d++)
{
for (var i = 0L; i <= 9; i++)
{
var x = i;
for (var t = 1; t < 18; t++)
{
var mod = x % 10;
if (mod + d < 0 || 10 <= mod + d) break;
x *= 10;
x += mod + d;
hashset.Add(x);
}
}
}
var answer = hashset.Where(x => x >= X).Min();
Console.WriteLine(answer);
}
E問題を解こうとしてたはずなのにF問題やってたのでコンテスト中には解けませんでした。コンテスト後に気づいて解いてみたら解けてたので悔しいです。
問題E
コンテスト中の考察
- 制約が
N<=5000
だからdpと組み合わせ? - 2^Nから重複を引く?
- 重複組み合わせ?
- 長さが
1
からN
のそれぞれの時に数え上げ?
for(var s = 1; s < S.Length; s++)
{
var sum = 0;
for(var i = 0; i < 26; i++)
{
sum = Math.Min(count[i], s);
}
// ToDo
}
解説では、i
番目までのアルファベットを使った時の文字列の合計がj
である時の組み合わせを数え上げる動的計画法でした。
public static void Solve()
{
var S = Scanner.Scan<string>();
var N = S.Length;
var fact = new mint[N + 1];
var ifact = new mint[N + 1];
fact[0] = ifact[0] = 1;
for (var i = 1; i <= N; i++)
{
fact[i] = fact[i - 1] * i;
ifact[i] = 1 / fact[i];
}
mint Combination(int n, int k)
{
if (n < k || n < 0 || k < 0) return 0;
return fact[n] * ifact[k] * ifact[n - k];
}
var count = new int[26];
foreach (var c in S)
{
count[c - 'a']++;
}
var dp = new mint[27, N + 1];
dp[0, 0] = 1;
for (var i = 0; i < 26; i++)
{
for (var j = 0; j <= N; j++)
{
for (var k = 0; k <= Math.Min(j, count[i]); k++)
{
dp[i + 1, j] += dp[i, j - k] * Combination(j, k);
}
}
}
mint answer = 0;
for (var i = 1; i <= N; i++)
{
answer += dp[26, i];
}
Console.WriteLine(answer);
}