AtCoder ABC 330 A～E を C# で


## 概要
久々の投稿になります。
[トヨタシステムズプログラミングコンテスト2023(AtCoder Beginner Contest 330)](https://atcoder.jp/contests/abc330)のC#実装例です。ロジックの解説は公式をご覧ください。

## A - Counting Passes
LINQ最高!
```cs
static void Main()
{
    var l = int.Parse(ReadLine().Split()[1]);
    var aa = ReadLine().Split().Select(int.Parse);
    
    WriteLine(aa.Count(a => a >= l));
}
```
## B - Minimize Abs 1
問題文の読解が最大の難関。
三項演算子が使えればスマートでしょう。
```cs
static void Main()
{
    var (n, l, r) = ReadLine().Split().Select(int.Parse);    
    var aa = ReadLine().Split().Select(int.Parse);
    
    WriteLine(string.Join(' ', aa.Select(a => a < l ? l : a < r ? a : r)));
}
```
## C - Minimize Abs 2
公式解説にあまり付け加えることがないです。
```cs
static void Main()
{
    var d = long.Parse(ReadLine());
    
    var xMax = (long)Math.Sqrt(d) + 1;
    
    var min = long.MaxValue;
    for (var x = 1L; x <= xMax; x++)
    {
        var x2 = x * x;
        var y = (long)Math.Sqrt(Math.Max(d - x2, 0));
        
        min = Math.Min(min, Math.Abs(x2 + y * y - d));
        min = Math.Min(min, Math.Abs(x2 + (y + 1) * (y + 1) - d));
    }
    WriteLine(min);
}
```
## D - Counting Ls
交点のマスに注目することに気づかずTLEしました。
countsBy～あたりはLINQの本領発揮といったところでしょうか。
最後のループはLINQでも書けますが、ベタに書いたほうがよいでしょう。
countをintで宣言すると死にます。
```cs
static void Main()
{
    var n = ReadLine();
    var ss = readLines().ToArray();

    var countsByRow = ss.Select(s => s.Count(c => c == 'o')).ToArray();
    var countsByColumn = Enumerable.Range(0, ss.Length).Select(i => ss.Count(s => s[i] == 'o')).ToArray();

    var count = 0L;
    for (var i = 0; i < ss.Length; i++)
    {
        for (var j = 0; j < ss.Length; j++)
        {
            if (ss[i][j] == 'o'){count += (countsByRow[i] - 1) * (countsByColumn[j] - 1);}
        }
    }
    WriteLine(count);
}
```
## E - Mex and Update
配列を更新しながらMEXを出力する問題。
データ構造の選択がポイントですが、C#ではSortedSet<T>が使えます。内部は二分木らしいので十分高速です。
なお間に合ったので省略しましたが、出力にバッファを挟んで1回のWriteで済ませることを検討したほうがよいでしょう。

```cs
static void Main()
{
    var (n, q) = ReadLine().Split().Select(int.Parse);
    var aa = ReadLine().Split().Select(int.Parse).ToArray();

    // Aの値と個数
    var counts = aa.GroupBy(a => a).ToDictionary(g => g.Key, g => g.Count());
    // Aに含まれない値
    var exs = new SortedSet<int>(Enumerable.Range(0, n + 1).Where(i => !counts.ContainsKey(i)));

    var ixs = readLines().Select(s => s.Split().Select(int.Parse).ToArray());
    
    foreach (var ix in ixs)
    {
        var (i, x) = ix;
        var pre = aa[i - 1];
        if (pre != x)
        {
            // 入れ替えして個数0・以外になったらexsを出し入れ
            aa[i - 1] = x;
            counts[pre]--;
            if (counts.ContainsKey(x) && counts[x] > 0) { counts[x]++; } else { counts[x] = 1; exs.Remove(x); }
            if (counts[pre] == 0) { exs.Add(pre); }
        }
        WriteLine(exs.Min);
    }
}
```

<details><summary>共通コード</summary>
もっと用意してありますが、今回使ったのはこのくらい。
Deconstructはいいですよね。

```cs
using System;
using System.Collections.Generic;
using System.Linq;
using static System.Console;

static class Program
{
    static IEnumerable<string> readLines()
    {
        for (var s = ReadLine(); !string.IsNullOrEmpty(s); s = ReadLine()){yield return s;}
    }

    // var (x, y) = ReadLine().Split(); と書けるようになる。
    static void Deconstruct<T>(this IEnumerable<T> source, out T item1, out T item2)
    {
        using var e = source.GetEnumerator();
        e.MoveNext(); item1 = e.Current;
        e.MoveNext(); item2 = e.Current;
    }
    
    static void Deconstruct<T>(this IEnumerable<T> source, out T item1, out T item2, out T item3)
    {
        using var e = source.GetEnumerator();
        e.MoveNext(); item1 = e.Current;
        e.MoveNext(); item2 = e.Current;
        e.MoveNext(); item3 = e.Current;
    }
}
```
</details>

以上、今回はコンテスト中に4完、解説読んでEを回答しました。精進します。