AtCoder ABC330をElixirで復習してみよう

[AtCoder Beginner Contest 330](https://atcoder.jp/contests/abc330)をElixirで解いてみます

# 問題A

リストAの中で、値がL以上のものが何個あるかカウントする問題。
Enumで解くのにピッタリ

```Elixir
defmodule Main do
    import Bitwise
    def next_token(acc \\ "") do
        case IO.getn(:stdio, "", 1) do
          " " -> acc
          "\n" -> acc
          x -> next_token(acc <> x)
        end
    end
    def input(), do: IO.read(:line) |> String.trim()
    def ii(), do: next_token() |> String.to_integer()
    def li(), do: input() |> String.split(" ") |> Enum.map(&String.to_integer/1)

    def main() do
        n = ii()
        l = ii()
        a = li()
        ans = Enum.count(a, fn x -> x >=l end)
        IO.puts(ans)
    end

end
```


# 問題B

この問題、Bなので簡単なはずなのに、問題を理解できず、プログラミング以前で悩みました。
10分は浪費しました。落ち着いて考えると、AiとL,Rの値からXiが決まります。

|条件|Xiの値|
|---|---|
|AiがLより小さい|L|
|AiがL~Rの間|Ai|
|AiがRより大きい|R|

この法則がわかれば、Elixirで書けます

方針
- 条件（この場合、L,R,Ai)の値を与えるとXiを返す関数(get_x)を作る
- Aのリスト全体にこの関数を適用してXを求める

get_xは、パターンマッチで書けそう。

リストの数値をIO.putsで表示する方法を忘れてたので、過去の回答から探してきました。

```Elixir
    def get_x(l,_r,a) when a<l, do: l
    def get_x(_l,r,a) when a>r, do: r
    def get_x(_l,_r,a), do: a

    def main() do
        n = ii()
        l = ii()
        r = ii()
        a = li()

        Enum.map(a, fn a -> get_x(l,r,a) end)
        |> Enum.join(" ")
        |> IO.puts()
    end
```

# 問題C

Xでは、尺取り法での解説をみかけました。私は、Pythonでは、二分探査で解いたので、二分探査で解いてみます。尺取り方をElixirで書くのって、難しそう。

方針
- xを与えたとき、x*x+y*y<Dを満たす最小のyを二分探査で求める関数get_y(x,d)を作成する
- xを0からsqrt(D)の範囲について、min(f(y),f(y+1))の最小値を求める
    y = get_y(x,d)
    f(y) = abs(x*x+y*y-D)
   
xの全探索は、for文のreduceで書いてみます

https://zenn.dev/koga1020/articles/903d6a6bdde26b

```Elixir
    def get_y(x,d,func,l,r) when r-l==1 do
        l
    end

    def get_y(x,d,func,l,r) do
        m = div((l+r),2)
        if func.(m) do
            get_y(x,d,func,m,r)
        else
            get_y(x,d,func,l,m)
        end
    end

    def get_y(x,d) do
        func = fn y -> x*x + y*y < d end
        l = 0
        r = floor(:math.sqrt(d)+1)
        get_y(x,d,func,l,r)
    end

    def xyd(x,y,d) do
        abs(x*x + y*y - d)
    end

    def main() do
        d = ii()
        for x <- 0..floor(:math.sqrt(d)), reduce: D do
            acc ->
                y = get_y(x,d)
                a = min(xyd(x,y,d), xyd(x,y+1,d))
                min(a, acc)
        end
        |> IO.puts()
    end
```
二分探査をElixirで初めて書いてみたんですが、再帰との相性がいいですね
TLEにならないか心配だったけど、1394msでACできました。


# 問題D

D問題になってくると、計算量の適切なアルゴリズムを思いつくのが一苦労です。
入力例を見ながら規則性がわかったところでプログラムにしてみます。

方針
- cnt_h[i] = i行のoの数のリストを作成
- cnt_w[j] = j列のoの数のリストを作成
- s[i][j]=="o"のi,jについて  (n_h[i] -1) * (n_w[j] -1)の合計を求める

方針の3つ目は、Pythonぽい書き方で書きましたが、実際次のようなPythonプログラムでACできます

```Python
ans = 0
for i in range(N):
    for j in range(N):
        if S[i][j] != "o":
            continue
        ans += (cnt_h[i] -1) * (cnt_w[j] -1)
```

Elixirのリストは、要素の参照がO(N)かかるので、使えません。tupleに変換してelemで要素を参照する方法もありますが、記述が結構大変。
次のようなプログラムにしてみました。

```Elixir
    def count_char(line,c) do
        Enum.count(line, fn x -> x == c end)
    end

    def count_o(s) do
      Enum.map(s, fn line -> count_char(line, ?o) end)
    end

    def transpose(s) do
        Enum.zip(s) |> Enum.map(&Tuple.to_list/1)
    end

    def main() do
        n = ii()
        s = for _ <- 1..n, do: input()|> String.to_charlist()
        cnt_h = count_o(s)
        cnt_v = count_o(transpose(s))

        flat_s = List.flatten(s)
        flat_cnt_v = List.duplicate(cnt_v, n) |> List.flatten()
        flat_cnt_h = Enum.flat_map(cnt_h, fn x -> List.duplicate(x, n) end)
        for {c, h, v} <- Enum.zip([flat_s, flat_cnt_h, flat_cnt_v]), reduce: 0 do
            acc -> if c == ?o, do: acc + (h-1) * (v-1), else: acc
        end
        |> IO.puts()
    end
```

残念ながらこのコードではTLEでした。
長さが2000*2000=4*10^6のリストを扱うことになるので、結構厳しそう。
Nxを使うとかStreamを使うとかが必要かもしれません。
ACできるコードは、また別途検討してみます。

# 問題E

Pythonでは、SortedSetを使って解きました。
Elixirではまだ未開拓な問題です。できるようになったら記事にしようとおもいます。