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 C# (.NET Core 3.1.201) (judge verdict: Accepted)

using System;
using System.Collections.Generic;
public class Hello{
    public static void Main(){
        BigInt bi = new BigInt(1, 23);
        bi.modulo_list[0] = 998244353;
        bi.primal_root_list[0] = 31;
        bi.make_basis();
        var line = Console.ReadLine().Split(' ');
        int N = int.Parse(line[0]), M = int.Parse(line[1]);
        var A = Console.ReadLine().Split(' ');
        var B = Console.ReadLine().Split(' ');
        int pl, dpt;
        int[] line1 = bi.powlimit(N+M);
        pl = line1[0];
        dpt = line1[1];
        long[] X1 = new long[pl];
        long[] X2 = new long[pl];
        for (int i=0;i<N;i++){
            X1[i] = long.Parse(A[i]);
        }
        for (int i=0;i<M;i++){
            X2[i] = long.Parse(B[i]);
        }
        long[] X1L = bi.NTT(X1, pl, dpt);
        long[] X2L = bi.NTT(X2, pl, dpt);
        long[] Y = new long[pl];
        for (int i=0;i<pl;i++){
            Y[i] = X1L[i]*X2L[i]%998244353;
        }
        long[] YL = bi.NTT(Y, pl, dpt, true);
        for (int i=0;i<N+M-1;i++){
            if (i != 0){
                Console.Write(" ");
            }
            Console.Write(YL[i]);
        }
        Console.WriteLine();
    }
}
public class BigInt{
    private int cr;
    private int bl;
    public long[] modulo_list;
    public long[] primal_root_list;
    public long[] bin_list;
    public long[] primal_base_matrix;
    public long[] inverse_base_matrix;
    public BigInt(int convolution_rank, int binary_level){
        this.cr = convolution_rank;
        this.bl = binary_level;
        this.modulo_list = new long[convolution_rank];
        this.primal_root_list = new long[convolution_rank];
        this.bin_list = new long[binary_level+1];
        this.primal_base_matrix = new long[convolution_rank*(binary_level+1)];
        this.inverse_base_matrix = new long[convolution_rank*(binary_level+1)];
        this.bin_list[0] = 1;
        for (int i=0;i<binary_level;i++){
            this.bin_list[i+1] = 2 * this.bin_list[i];
        }
        for (int i=0;i<convolution_rank;i++){
            this.modulo_list[i] = 1;
            this.primal_root_list[i] = 1;
            for (int j=0;j<=binary_level;j++){
                this.primal_base_matrix[i*(binary_level+1)+j] = 0;
                this.inverse_base_matrix[i*(binary_level+1)+j] = 0;
            }
        }
    }
    public long doubling(long n, long m, long modulo=0){
        long y = 1;
        long bas = n;
        long tmp = m;
        while (tmp!=0){
            if (tmp % 2 == 1){
                y *= bas;
                if (modulo!=0){
                    y %= modulo;
                }
            }
            bas *= bas;
            if (modulo!=0){
                bas %= modulo;
            }
            tmp /= 2;
        }
        return y;
    }
    public int[] powlimit(long n){
        int[] a = new int[2];
        a[0] = 1;
        a[1] = 0;
        while (a[0] < n){
            a[0] *= 2;
            a[1] += 1;
        }
        return a;
    }
    public bool IsPrime(long num){
        long p = 2;
        if (num <= 1){
            return false;
        }
        while (p * p <= num){
            if (num % p == 0){
                return false;
            }
            p += 1;
        }
        return true;
    }
    private long safety_divisor(long a, long b, bool modulo=false){
        if (b == 0){
            Console.Error.WriteLine("ValueError: ZeroDivision");
        }
        if (a % b < 0){
            if (modulo){
                return b + a % b;
            }
            return a / b - 1;
        }
        if (modulo){
            return a % b;
        }
        return a / b;
    }
    public long[] extgcd(long a, long b, long c){
        if (b < 0){
            a = -a;
            b = -b;
            c = -c;
        }
        long x, y, u, v, k, l, tmp1, tmp2, tmp3;
        x = 1;
        y = 0;
        u = 0;
        v = 1;
        k = a;
        l = b;
        while (l!=0){
            tmp1 = x;
            tmp2 = y;
            tmp3 = k;
            x = u;
            y = v;
            u = tmp1 - u * safety_divisor(k, l);
            v = tmp2 - v * safety_divisor(k, l);
            k = l;
            l = safety_divisor(tmp3, l, true);
        }
        if (c % k != 0){
            Console.Error.WriteLine("No Solution");
        }
        long[] sol = new long[2];
        sol[0] = c*x;
        sol[1] = c*y;
        return sol;
    }
    public long inved(long a, long modulo){
        long[] x = extgcd(a, modulo, 1);
        return (x[0]+modulo)%modulo;
    }
    public void make_basis(){
        long tmp;
        for (int i=0;i<this.cr;i++){
            for (int j=0;j<=this.bl;j++){
                tmp = doubling(2, this.bl-j);
                this.primal_base_matrix[i*(this.bl+1)+j] = doubling(primal_root_list[i], tmp, modulo_list[i]);
                this.inverse_base_matrix[i*(this.bl+1)+j] = inved(primal_base_matrix[i*(this.bl+1)+j], modulo_list[i]);
            }
        }
    }
    public long[] NTT(long[] f, int n, int depth, bool inverse=false){
        long[] res = new long[n];
        long[] tmp = new long[n];
        long MOD = 998244353;
        long ipl = inved(n, MOD);
        long pos;
        long grow, seed;
        for (int i=0;i<n;i++){
            pos = 0;
            for (int j=depth;j>0;j--){
                pos += (((i>>(j-1))&1) << (depth-j));
            }
            res[i] = f[pos];
        }
        long basl = 1;
        long basr = 1 << (depth - 1);
        long right;
        for (int i=0;i<depth;i++){
            grow = 1;
            if (inverse){
                seed = primal_base_matrix[i+1];
            }
            else{
                seed = inverse_base_matrix[1+i];
            }
            for (int k=0;k<basl;k++){
                for (int j=0;j<basr;j++){
                    pos = 2*j*basl+k;
                    right = (grow * res[pos+basl]) % MOD;
                    tmp[pos] = (res[pos] + right) % MOD;
                    tmp[pos+basl] = (MOD + res[pos] - right) % MOD;
                }
                grow *= seed;
                grow %= MOD;
            }
            basl <<= 1;
            basr >>= 1;
            for (int j=0;j<n;j++){
                res[j] = tmp[j];
            }
        }
        if (inverse){
            for (int i=0;i<n;i++){
                res[i] *= ipl;
                res[i] %= MOD;
            }
        }
        return res;
    }
}