//https://l...content-available-to-author-only...j.ac/p/108
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
#define pb push_back
#define ff first
#define ss second
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ld, ld> pld;
const int INF = 1e9;
const ll LLINF = 1e18;
const int MOD = 1e9 + 7;
template<class K> using sset = tree<K, null_type, less<K>, rb_tree_tag, tree_order_statistics_node_update>;
inline ll ceil0(ll a, ll b) {
return a / b + ((a ^ b) > 0 && a % b);
}
void setIO() {
ios_base::sync_with_stdio(0); cin.tie(0);
}
const double PI = acos(-1);
typedef complex<ld> cint;
//Returns a complex number in the form of e^2*PI*k*i/n (i is an imaginary number)
cint root(int n, int k){
return exp(cint(0, 2*PI*k/n));
}
//Returns the bitwise reverse of x using only the first mx bits
int rev(int x, int mx){
int ret = 0;
for(int i = 0; i <= mx; i++){
if(x & (1 << i)) ret |= 1 << (mx - i);
}
return ret;
}
/**
* Finds the Discrete Fourier Transform (DFT) of a polynomial using the
* Fast Fourier Transform (FTT) method in O(n log n) time complexity.
*
* Definitions:
*
* root(n, k) = e^(2*PI*i*k/n), where i is an imaginary number
* X = {x_1, x_2, x_3, ..., x_n}
* Y = {y_1, y_2, y_3, ..., y_n}
* F(x) = {x_1, x_2 * x, x_3 * x^2, ..., x_n * x^(n - 1)}
*
* DFT(X) = Y, defined as follows
*
* y_1 = F(root(n, 0))
* y_2 = F(root(n, 1))
* y_3 = F(root(n, 2))
* ...
* y_n = F(root(n, n - 1))
*
* But how can we calculate Y, when DFT(X) = Y?
*
* Solution:
* First, we see that we can split F(x) into even and odd parts, lets call them E(x), and O(x)
*
* O(x) = x_1 + x_3 * x + x_5 * x^2 ... x_(n - 1) * x^(n/2 - 1)
* E(x) = x_2 + x_4 * x + x_6 * x^2 ... x_n * x^(n/2 - 1)
*
* Then, F(x) becomes
*
* F(x) = O(x^2) + x * E(x^2)
*
* Since y_i = F(root(n, i - 1)), y_i becomes
*
* y_i = O(root(n, i - 1)^2) + root(n, i - 1) * E(root(n, i - 1)^2)
*
* Simplifying further, this becomes
*
* y0_i = O(root(n/2, i - 1)^2) = O(root(n, i - 1))
* y1_i = E(root(n/2, i - 1)^2) = E(root(n, i - 1))
* y_i = y0_i + root(n, i)*y1_i, i < n/2
* y_(n/2 + i) = y0_i + root(n, n/2 + i)*y1_i, n/2 <= i
*
* From here, we can find Y recursively. Since every split halves the n, and every merge takes O(n) complexity,
* amortized time complexity is O(n log n)
*
* To calculate invDFT, it is the same as calculating DFT with only a few changes. Simply negate k when computing
* root(n, k), as well as dividing the final answer by n. This can be proven by using inverse matrices.
*
* @param v The polynomial to perfom FFT on. The size of v should be a power of 2
* @param sign Tndicates whether or not the function computes the
* DFT if sign is 1 or invDFT if sign is -1.
*/
void fft(vector<cint> &v, int sign){
int n = v.size(), lg = log2(n) - 1;
cint tmp[n];
/**
* swaps each position with its bitwise inverse.
* This step is needed to ensure correct ordering for the iterative implementation,
* as some of the even and odd indices are swapped every iteration.
*/
for(int i = 0; i < n; i++){
if(i < rev(i, lg)) swap(v[i], v[rev(i, lg)]);
}
for(int i = 2; i <= n; i *= 2){
//compute each segment of length i
for(int j = 0; j < n; j += i){
for(int k = 0; k < i/2; k++){
//computes the left half of the segment using the formula described above
tmp[k] = v[j + k] + root(i, sign*k)*v[j + i/2 + k];
//computes the right half of the segment using the formula described above
tmp[i/2 + k] = v[j + k] + root(i, sign*(i/2 + k))*v[j + i/2 + k];
}
//updates the actual values to the new values
for(int k = 0; k < i; k++) v[j + k] = tmp[k];
}
}
//If we are computing invDFT, we must divide every value by n
if(sign == -1) for(cint &i : v) i /= n;
}
/**
* Multiplies two polynomials using Discrete Fourier Transform (DFT) in O(n log n) time complexity.
*
* Definitions:
* X = {x_1, x_2, x_3, ..., x_n}
* Y = {y_1, y_2, y_3, ..., y_n}
*
* DFT(X) = Y
* invDFT(Y) = X
* invDFT(DFT(X)) = X
*
* X * Y = the product of polynomials X and Y
* DFT(X) * DFT(Y) = {x_1 * y_1, x_2 * y_2, x_3 * y_3, ..., x_n * y_n}
* DFT(X * Y) = DFT(X) * DFT(Y)
*
* Solution:
* Given two vectors A and B, lets define their product vector as ANS = A * B
* Then, using the distributive property, we can see that
*
* DFT(A * B) = DFT(A) * DFT(B)
* DFT(ANS) = DFT(A) * DFT(B)
*
* Applying invDFT to both sides, we get
*
* ANS = invDFT(DFT(A) * DFT(B))
*
* Performing DFT(A) * DFT(B) is much simpler than A * B, and can be done in O(n) time complexity
* We can calculate DFT and invDFT in O(n log n) time complexity, giving us a total complexity of O(n log n)
*
* @param a the first polynomial
* @param b the second polynomial
* @return returns the polynomial a * b
*/
vector<ll> mult(const vector<ll> &a, const vector<ll> &b){
//resizes the answer to a power of 2
int n = 1;
while(n < a.size() + b.size()) n *= 2;
//constructs two copies of polynomials a and b, but using complex numbers instead
vector<cint> aa(a.begin(), a.end()), bb(b.begin(), b.end());
//resize the polynomials to have a size with a power of 2
aa.resize(n), bb.resize(n);
//computes DFT(a) and DFT(b)
fft(aa, 1), fft(bb, 1);
//sets a = DFT(a) * DFT(b) as described above
for(int i = 0; i < n; i++) aa[i] *= bb[i];
//returns a to a polynomial by setting a = invDFT(a)
fft(aa, -1);
//extracts the answer from the final polynomial. The values must be rounded due to double precision
vector<ll> ret(n);
for(int i = 0; i < n; i++){
ret[i] = round(aa[i].real());
}
return ret;
}
int main(){
setIO();
int n, m;
cin >> n >> m;
n++, m++;
vector<ll> v1(n), v2(m);
for(int i = 0; i < n; i++) cin >> v1[i];
for(int i = 0; i < m; i++) cin >> v2[i];
vector<ll> ans = mult(v1, v2);
//for(auto i : ans) cout << i << endl;
for(int i = 0; i < n + m - 1; i++) cout << ans[i] << " ";
cout << endl;
}