Problem:
Score : 100 points

Problem Statement

You are given two integer arrays a_0, a_1, ..., a_{N - 1} and b_0, b_1, ..., b_{M - 1}. Calculate the array c_0, c_1, ..., c_{(N - 1) + (M - 1)}, defined by c_i = \sum_{j = 0}^i a_j b_{i - j} \bmod 998244353.

Constraints

1 \leq N, M \leq 524288

0 \leq a_i, b_i < 998244353

All values in Input are integer.

Input

Input is given from Standard Input in the following format:

N M
a_0 a_1 ... a_{N-1}
b_0 b_1 ... b_{M-1}

Output

Print the answer in the following format:

c_0 c_1 ... c_{(N - 1) + (M - 1)}

Sample Input 1

4 5
1 2 3 4
5 6 7 8 9

Sample Output 1

5 16 34 60 70 70 59 36

Sample Input 2

1 1
10000000
10000000

Sample Output 2

871938225

Solution in PHP (7.4.4) (judge verdict: Accepted)

<?php

/**
 * Class Convolution
 * ref.AtCoder Library
 * 畳み込みを行います。数列 a_0, a_1, ..., a_{N - 1)と
 * 数列 b_0, b_1, ..., b_{M - 1}から、長さ N + M - 1 の数列
 * c_i = Σ(j=0 -> i) a_j･b_{i - j}を計算します。
 */
class Convolution
{

    private int $mod;

    /**
     * 畳み込みを \bmod mmodm で計算します。a, ba,b の少なくとも一方が空配列の場合は空配列を返します。
     * 計算量 n = |a| +|b|として
     * O(nlog(n) + log(mod))
     * 制約
     * 2^c∣(m−1) かつ |a| + |b| - 1 ≤ 2^cなる c が存在する
     * @param array $a
     * @param array $b
     * @param int $MOD 2 ≤ m ≤ 2 x 10^9, mは素数
     * @return array
     */
    public function convolution_I(array $a, array $b, int $MOD = 1000000007): array
    {
        $n = count($a);
        $m = count($b);
        $this->mod = $MOD;
        if (!$n || !$m) {
            return [];
        }

        if (min($n, $m) <= 60) {
            if ($n < $m) {
                [$n, $m] = [$m, $n];
                [$a, $b] = [$b, $a];
            }
            $ans = array_fill(0, $n + $m - 1, 0);
            for ($i = 0; $i < $n; $i++) {
                for ($j = 0; $j < $m; $j++) {
                    $ans[$i + $j] += ($a[$i] * $b[$j]) % $this->mod;
                    $ans[$i + $j] %= $this->mod;
                }
            }
            return $ans;
        }
        $z = 1 << $this->ceil_log2($n + $m - 1);
        for ($i = count($a); $i < $z; $i++) {
            $a[$i] = 0;
        }
        for ($i = count($b); $i < $z; $i++) {
            $b[$i] = 0;
        }

        $g = $this->primitive_root($this->mod);
        $es = $ies = $sum_e = array_fill(0, 30, 0);
        $cnt2 = $this->NTZ($this->mod - 1);
        $e = (int)gmp_powm($g, ($this->mod - 1) >> $cnt2, $this->mod);
        $ie = (int)gmp_invert($e, $this->mod);
        for ($i = $cnt2; $i >= 2; $i--) {
            $es[$i - 2] = $e;
            $ies[$i - 2] = $ie;
            $e =  $e * $e % $this->mod;
            $ie = $ie * $ie % $this->mod;
        }
        $now = 1;
        for ($i = 0; $i < $cnt2 - 2; $i++) {
            $sum_e[$i] = ($es[$i] * $now) % $this->mod;
            $now = $now * $ies[$i] % $this->mod;
        }

        $now = 1;
        for ($i = 0; $i < $cnt2 - 2; $i++) {
            $sum_ie[$i] = ($ies[$i] * $now) % $this->mod;
            $now = $now * $es[$i] % $this->mod;
        }

        $a = $this->butterfly($a, $sum_e);

        $b = $this->butterfly($b, $sum_e);
        for ($i = 0; $i < $z; $i++) {
            $a[$i] = $a[$i] * $b[$i] % $this->mod;
        }
        $a = $this->butterfly_inv($a, $sum_ie);
        $iz = (int)gmp_invert($z, $this->mod);
        $ans = [];
        for ($i = 0; $i < $n + $m - 1; $i++) {
            $ans[$i] = (($a[$i] + $this->mod) * $iz) % $this->mod;

        }
        return $ans;
    }

    /**
     * 畳み込みを計算します。a, b の少なくとも一方が空配列の場合は空配列を返します。
     * 計算量 n = |a| + |b| として
     * O(nlog(n))
     * 制約
     * |a| + |b| - 1 ≤ 2^24 (≤ 10^8)
     * 畳み込んだ後の配列の要素が全てllに収まる
     * @param array $a
     * @param array $b
     * @return array
     */
    public function convolution_II(array $a, array $b)
    {
        $n = count($a);
        $m = count($b);
        $MOD1 = 754974721;
        $MOD2 = 167772161;
        $MOD3 = 469762049;
        $M2M3 = $MOD2 * $MOD3;
        $M1M3 = $MOD1 * $MOD3;
        $M1M2 = $MOD1 * $MOD2;
        $M1M2M3 = $M1M2 * $MOD3;
        $i1 = $this->inv_gcd($M2M3, $MOD1)[1];
        $i2 = $this->inv_gcd($M1M3, $MOD2)[1];
        $i3 = $this->inv_gcd($M1M2, $MOD3)[1];
        $c1 = $this->convolution_I($a, $b, $MOD1);
        $c2 = $this->convolution_I($a, $b, $MOD2);
        $c3 = $this->convolution_I($a, $b, $MOD3);
        $c = array_fill(0, $n+$m-1,0);
        $offset = [0, 0, $M1M2M3, 2* $M1M2M3, 3 * $M1M2M3];
        for($i=0;$i<$n+$m-1;$i++){

            $x = ($c1[$i] * $i1) % $MOD1 * $M2M3
                + ($c2[$i] * $i2) % $MOD2 * $M1M3
                + ($c3[$i] * $i3) % $MOD3 * $M1M2;
            $diff = ($MOD1 + $c1[$i] - $x % $MOD1) % $MOD1;
            $x -= $offset[$diff % 5];
            $c[$i] = $x;
        }
        return $c;
    }

    private function inv_gcd(int $a, int $b): array
    {
        $a = (int)gmp_mod($a, $b);
        if ($a === 0) {
            return [$b, 0];
        }
        $s = $b;
        $t = $a;
        $m0 = 0;
        $m1 = 1;
        while ($t) {
            $u = intdiv($s, $t);
            $s -= $t * $u;
            $m0 -= $m1 * $u;
            [$s, $t] = [$t, $s];
            [$m0, $m1] = [$m1, $m0];

        }
        if ($m0 < 0) {
            $m0 += intdiv($b, $s);
        }
        return [$s, $m0];
    }

    private function butterfly(array $a, array $sum_e):array
    {
        $n = count($a);
        $h = $this->ceil_log2($n);
        $p = 1 << $h;
        $w = 1;
        for ($ph = 1; $ph <= $h; $ph++) {
            $p >>= 1;
            $now = 1;
            $offset = 0;
            $add = 1 << ($h - $ph + 1);
            for ($s = 0; $s < $w; $s++) {
//                $offset = $s << ($h - $ph + 1);
                for ($i = 0; $i < $p; $i++) {

                    $a[$i + $offset + $p] =( $a[$i + $offset] - $a[$i + $offset + $p] * $now )% $this->mod;
                    $a[$i + $offset] = ($a[$i + $offset] * 2 - $a[$i + $offset + $p]) % $this->mod;
                }
                $now = $now * $sum_e[$this->NTZ(~($s))] % $this->mod;
                $offset += $add;
            }
            $w <<= 1;
        }
        return $a;
    }

    private function butterfly_inv(array $a, array $sum_ie):array
    {
        $n = count($a);
        $h = $this->ceil_log2($n);

        $w = 1 << $h;
        $p = 1;
        for ($ph = $h; $ph >= 1; $ph--) {
            $w >>= 1;
            $inow = 1;
            $offset = 0;
            $add = 1 << ($h - $ph + 1);
            for ($s = 0; $s < $w; $s++) {
                for ($i = 0; $i < $p; $i++) {
                    $a[$i + $offset] = ($a[$i + $offset] + $a[$i + $offset + $p])  % $this->mod;

                    $a[$i + $offset + $p] = (($a[$i + $offset] - ($a[$i + $offset + $p]<<1))  * $inow) % $this->mod;
                }
                $inow = $inow * $sum_ie[$this->NTZ(~$s)] %$this->mod;
                $offset += $add;
            }
            $p <<= 1;
        }
        return $a;
    }

    private function bit_to_num(int $bits): int
    {
        $bits = ($bits & 0x55555555) + ($bits >> 1 & 0x55555555);
        $bits = ($bits & 0x33333333) + ($bits >> 2 & 0x33333333);
        $bits = ($bits & 0x0f0f0f0f) + ($bits >> 4 & 0x0f0f0f0f);
        $bits = ($bits & 0x00ff00ff) + ($bits >> 8 & 0x00ff00ff);
        return ($bits & 0x0000ffff) + ($bits >> 16 & 0x0000ffff);
    }

    /**
     * bitの末尾に連続する0の数を数える
     * 110110000 -> 4
     * 11100 -> 2
     * @param int $bits
     * @return int
     */
    private function NTZ(int $bits): int
    {
        return $this->bit_to_num((~$bits) & ($bits - 1));
    }

    public function primitive_root(int $m): int
    {
        if ($m === 2) return 1;
        if ($m === 167772161) return 3;
        if ($m === 469762049) return 3;
        if ($m === 754974721) return 11;
        if ($m === 998244353) return 3;
        $divs = array_fill(0, 20, 0);
        $divs[0] = 2;
        $cnt = 1;
        $x = intdiv($m - 1, 2);
        while ($x % 2 === 0) {
            $x >>= 1;
        }
        for ($i = 3; $i * $i <= $x; $i += 2) {
            if ($x % $i === 0) {
                $divs[$cnt++] = $i;
                while ($x % $i === 0) {
                    $x /= $i;
                }
            }
        }
        if ($x > 1) {
            $divs[$cnt++] = $x;
        }
        for ($g = 2; ; $g++) {
            $ok = true;
            for ($i = 0; $i < $cnt; $i++) {
                if ((int)gmp_powm($g, intdiv(($m - 1), $divs[$i]), $m) === 1) {
                    $ok = false;
                    break;
                }
            }
            if ($ok) return $g;
        }
        return 0;
    }

    private function ceil_log2(int $x): int
    {
        $t = [0xFFFFFFFF00000000,
            0x00000000FFFF0000,
            0x000000000000FF00,
            0x00000000000000F0,
            0x000000000000000C,
            0x0000000000000002];
        $y = ((($x & ($x - 1)) == 0) ? 0 : 1);
        $j = 32;
        foreach ($t as $value) {
            $k = ((($x & $value) == 0) ? 0 : $j);
            $y += $k;
            $x >>= $k;
            $j >>= 1;
        }
        return $y;
    }
}


[$N, $M] = fscanf(STDIN, "%d%d");
$a = $b = [];

$a = fscanf(STDIN, str_repeat("%d", $N));

$b = fscanf(STDIN, str_repeat("%d", $M));
$con = new Convolution();
$c = $con->convolution_I($a, $b, 998244353);
foreach($c as $value){
    echo $value . " ";
}