AtCoder をやるときはスタックサイズを調整しとけ

# きっかけ
先日、皆さんご存じ **AtCoder** でこんな問題が出題されました。

https://atcoder.jp/contests/abc307/tasks/abc307_e

問題の内容は長くなるので省略しますが、再帰関数を使って解ける問題です。ちなみに私が書いたのは[この解説](https://atcoder.jp/contests/abc307/editorial/6674)と同じものでした。

```c++
#include "atcoder/modint.hpp"
#include <iostream>

using mod_int = atcoder::modint998244353;

mod_int solve(int n, int m)
{
    return n == 1 ? 0 : m * mod_int{m - 1}.pow(n - 1) - solve(n - 1, m);
}

int main()
{
    int n, m;
    std::cin >> n >> m;
    std::cout << solve(n, m).val() << std::endl;
}
```

理屈には完璧な自信がありました。しかし一部のテストケースでエラーが発生してしまいます。

```bash
$ ./a.out
987654 456789
Segmentation fault
```

このプログラムは全部で `n` 回の再帰を行うので、`n` が大きすぎるとスタックを使い果たしてしまうようです。しかし諦めが悪い私はヤケクソになって提出しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507500/057d30ae-845f-f1f0-3853-bb1df329d6a5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507500/c461bbd6-886e-7255-bd6e-96e161c0bc9c.png)

# 何が起こった？
AtCoder 内部で使用されているコンパイラオプションは以下で公開されています。

https://atcoder.jp/contests/APG4b/rules

なので、私は「このオプションをコピペしておけば AtCoder 内部と**全く同じ環境**でプログラムを動かせる」と思い込んでいました。しかしそれは**大きな誤り**でした。

## AtCoder 環境はスタックサイズがわりとデカい
先ほど私の環境で Segmentation fault を起こしたプログラムですが、ｾｸﾞﾌｫの原因がスタック不足であるならばスタックを増やしてやればきちんと動くはずです。まずは現時点でのスタックサイズの確認です。

```bash
$ ulimit -s
8192
```

8192 キロバイトでした。

一方の AtCoder はどうでしょうか。AtCoder は Bash にも対応しているため、コードテストページから `ulimit -s` を実行することでスタックサイズを知ることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507500/d0323be4-c405-5d62-c1a5-87662f094a47.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507500/71390e8b-6ffd-77c9-7172-98c99ae19fa9.png)
**1048576 キロバイト**。私の環境の実に 128 倍です。

ところでさっき私が解こうとしていた問題のメモリ制限のところを見てみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507500/42464f66-f58f-e22f-bdf8-80058cff9ca8.png)
メモリ制限の 1024 メガバイトとは、すなわち 1048576 キロバイトです。最初からここに書いてあったんですね。

:::note
「え？1024 メガバイトと 1048576 キロバイトってだいぶ違くない？」とお思いの方もいるかもしれません。ここには「キロバイト」や「メガバイト」などをめぐるややこしい事情が存在します。

そもそも「キロ」や「メガ」は **SI 接頭辞**といい、それぞれ「1000 倍」や「1000000 倍」を意味する言葉です。さらに 1000000000 倍なら「ギガ」、1000000000000 倍なら「テラ」と続きます。

ところがデータ容量を表す場合に限って**厄介な俗習**が存在するのです。プログラマーという特殊な生き物は「十進法より二進法を好む」という**特殊な嗜好**を持ちますので、あるときから「キロ」が勝手に **1024 倍** という意味で使われ出してしまったのです。これと同じ調子で「メガ」は 1048576 倍、「ギガ」は 1073741824 倍……と、2 の冪に合わせる用法が広がってしまいました。こういうわけで、「1 キロバイト」と言った場合「1000 バイト」を指す場合と「1024 バイト」を指す場合の二つが存在することになってしまったのです。

この混乱を避けるため、2 の冪を基準とするデータ容量は**二進接頭辞**で表すという流儀も存在します。即ち 1024 バイトは **1 キビバイト (KiB)**、1048576 バイトは **1 メビバイト (MiB)** …… という具合です。ですが残念ながらこちらはあまり普及しているとは言えません。

話を戻しますと、今回の問題におけるメモリ制限は正確には「1024 メビバイト」だったということであり、これは先ほど `ulimit -s` で見たスタックサイズ「1048576 キビバイト」と厳密に一致します。
:::

そういうわけですから、手元の環境でもスタックサイズを変更して試してみましょう。

```bash
$ ulimit -S -s 1048576
$ ./a.out
987654 456789
778634319
```

無事にちゃんと動きました。

# ほかの解決策
## 1. 末尾再帰最適化が効くようにする
**末尾再帰最適化**が効くようなコードを書くことで、定数容量のメモリで任意回の再帰が行えるようになります。詳しい解説は[こちらの記事](https://qiita.com/pebblip/items/cf8d3230969b2f6b3132)をご覧ください。今回の問題に強引に末尾再帰を適用するとこのようなコードになります。

```c++
#include "atcoder/modint.hpp"
#include <iostream>

using mod_int = atcoder::modint998244353;

int n, m;

mod_int tail_recursive_func(int _n, mod_int acc)
{
    return _n > n ? acc : tail_recursive_func(_n + 1, m * mod_int{m - 1}.pow(_n - 1) - acc);
}

int main()
{
    std::cin >> n >> m;
    std::cout << tail_recursive_func(2, 0).val() << std::endl;
}
```

このように、実に 10 倍近くメモリを削減できています。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507500/e3caab0b-559e-2e37-0750-0290dc005ecb.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507500/8356ff0b-ea36-c8e4-ba62-d7877d9defec.png)|
|:---:|:---:|
|単純な再帰バージョン|末尾再帰バージョン|

## 2. そもそも再帰すんな
一番安全かつ簡単なのはこちらでしょうか。そもそも深すぎる再帰はその分関数呼び出しのオーバーヘッドも増えるので、競技プログラミングでは不利と言わざるを得ません。

```c++
#include "atcoder/modint.hpp"
#include <iostream>

using mod_int = atcoder::modint998244353;

int main()
{
    int n, m;
    std::cin >> n >> m;
    mod_int ans = 0;
    for (int i = 2; i <= n; ++i) {
        ans = m * mod_int{m - 1}.pow(i - 1) - ans;
    }
    std::cout << ans.val() << std::endl;
}
```

実行時間から見ても消費メモリから見ても末尾再帰バージョンと大差ないようです。再帰回数が絶対に少なくて済むと言い切れるとき以外は再帰しないのが無難っぽいですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507500/edc0fbd9-8870-3fed-1430-1b2798f09da7.png)

# 教訓
<font color=#55C500><b>諦めたらそこで試合終了ですよ。</b></font>