Problem:
nmマス計算
あなたは小学校の情報の先生である．職員室で担任の先生と話をしていると，今年の夏休みの宿題として 100 マス計算を拡張した
nm
マス計算が出されたと聞いた．
宿題は児童が他の児童の回答を写して提出するのを防ぐため，また各児童の習熟度に合わせて課題の難易度調整を行うために，各児童には異なる計算課題が出題された．具体的には以下の通りである．
計算結果の記入部が
n
行
m
列ある方眼用紙が配られる．
更に長さ
n
の整数列
a
, 長さ
m
の整数列
b
も配られる．これは
nm
マス計算の縦部と横部に対応する．
計算結果のマスは左上が
(1,1)
，左下が
(n,1)
，右下が
(n,m)
となっている．
児童はマス
(i, j)
に対して
a
i
× b
j
の結果を 10 進法表記で書き込む．このとき，先頭に不必要な 0 をつけることはない．
例えば縦部
a = {10, 2, 5}
, 横部
b = {1, 1, 6}
として出題された児童は，図 A-1 の様にマスに記入することになる．
図 A-1
nm
マス計算の例
さて夏休みの宿題を出したは良いものの，評価のためには児童が正しく課題をこなしたか確認する必要がある．しかし先生の仕事は忙しいので，沢山いる児童の
nm
マス計算の結果を検算するのは大変である．そこで児童には，計算結果の記入部に記入した数字 0-9 の個数を別に提出してもらうことにして，この個数が合っているかで評価することにした．
 図 A-1 を例にすると，0 は 4 個，1 は 3 個，2 は 3 個，3 は 1 個，5 は 2 個，6 は 1 個，他は 0 個である．
しかしこの個数を求めるのも一苦労である．万が一この想定解を間違えて児童に誤った評価を下そうものなら児童の人生を左右してしまうし，先生も PTA からのお叱りを受けてしまう．
 そこで担任の先生は，優秀な情報の先生であるあなたに，正解解答を作成してもらうことにした．あなたは解答を求めるプログラムを書くことにした．
Input
入力は次の形式の最大 100 個のデータセットからなる．各データセットは次の形式で表される．
n
m
a
1
a
2
...
a
n
b
1
b
2
...
b
m
データセットの最初の行は，二つの整数
n
と
m
からなる．
n
は縦の行数，
m
は横の列数である．この値は
1 ≤ n, m ≤ 100
を満たす．
2 行目は整数列
a
の値が
n
個ある．
a
i
(
1 ≤ i ≤ n
) は
i
行目の値を表す．
3 行目は整数列
b
の値が
m
個ある．
b
j
(
1 ≤ j ≤ m
) は
j
列目の値を表す．
これらの値は
1 ≤ a
i
, b
j
≤ 1,000
を満たす．
入力の終わりはゼロふたつからなる行で表される．
Output
各データセットについて，与えられた
nm
マス計算に書き込まれる数字 0–9 の個数
c
0
c
1
... c
9
を 1 行に空白区切りで出力せよ．
Sample Input
2 4
2 1
1 4 2 3
3 3
10 2 5
1 1 6
10 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
8 6
3 1 4 1 5 9 2 6
2 7 1 8 2 8
8 15
3 10 5 16 8 4 2 1
11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
0 0
Output for the Sample Input
0 1 2 1 2 0 1 0 1 0
4 3 3 1 0 2 1 0 0 0
28 24 27 15 23 15 17 8 15 6
4 12 16 5 11 2 6 4 11 1
59 42 41 20 28 18 29 7 23 1

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;

int main() {
    while(true){
        int n,m;
        cin >> n >> m;
        if(!n && !m){
            break;
        }
        vector<int> a(n);
        vector<int> b(m);
        for(int i=0;i<n;i++){
            cin >> a.at(i);
        }
        for(int i=0;i<m;i++){
            cin >> b.at(i);
        }
        vector<int> ans(10);
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                int mu = a.at(i) * b.at(j);
                while(mu){
                    ans.at(mu%10)++;
                    mu /= 10;
                }
            }
        }
        for(int i=0;i<10;i++){
            cout << ans.at(i) << (i == 9 ? '\n' : ' ');
        }
    }
    
    
}
accuracy: 1
time_ms: 10
memory_kb: 3180
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using a2 = array<ll, 2>;
using a3 = array<ll, 3>;

template <typename A>
void chmin(A &l, const A &r) {
    if (r < l)
        l = r;
}
template <typename A>
void chmax(A &l, const A &r) {
    if (l < r)
        l = r;
}

ll mod = 998244353;

void solve(ll n, ll m) {
    vector<ll> v(n), w(m);
    vector<ll> ans(10, 0);
    for (auto &x : v) cin >> x;
    for (auto &x : w) cin >> x;
    for (auto &x : v) {
        for (auto &y : w) {
            ll a = x * y;
            string s = to_string(a);
            for (auto &z : s) {
                ans[z - '0']++;
            }
        }
    }

    for (int i=0;i<10;i++)
        cout << ans[i] << (i==9?'\n':' ');
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    while (1) {
        ll n, m;
        cin >> n >> m;
        if (n == 0)
            break;
        solve(n, m);
    }

    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3532
final_rank: 16

code_snippet: #include <bits/stdc++.h>

using namespace std;
int main()
{
	int n,m;
	while(1)
	{
		cin >> n >> m;
		if(n==0 && m==0) break;

		vector<int> cnt(10,0);
		vector<int> c(n),r(m);
		for(int i=0; i<n; i++)
		{
			cin >> c[i];
		}
		for(int i=0; i<m; i++)
		{
			cin >> r[i];
		}

		for(int i=0; i<n; i++)
		{
			for(int j=0; j<m; j++)
			{
				int calc = c[i] * r[j];
				string s = to_string(calc);
				
				for(auto elem :s)
				{
					cnt[elem - '0']++;
				}
			}
		}

		for(int i=0; i<9; i++)
		{
			cout << cnt[i] << " ";
		}
		cout << cnt[9] << endl;
	}
	return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3504
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define loop(n) for (int tjh = 0; tjh < (int)(n); tjh++)
constexpr int maxn=100;
int n,m;array<int,maxn>a,b;
array<int,10>res;
string s;
signed main() {
  cin.tie(0)->sync_with_stdio(0);
  cout<<fixed<<setprecision(16);
  for(;cin>>n>>m,n;){
    ranges::fill(res,0);
    rep(i,n)cin>>a[i];
    rep(i,m)cin>>b[i];
    rep(i,n)rep(j,m){
      s=to_string(a[i]*b[j]);
      for(char k:s)res[k-48]++;
    }
    rep(i,10)cout<<res[i]<<(i==9?'\n':' ');
  }
}
accuracy: 1
time_ms: 10
memory_kb: 3456
final_rank: 6

code_snippet: # include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
const double pi = acos(-1);
template<class T>constexpr T inf() { return ::std::numeric_limits<T>::max(); }
template<class T>constexpr T hinf() { return inf<T>() / 2; }
template <typename T_char>T_char TL(T_char cX) { return tolower(cX); }
template <typename T_char>T_char TU(T_char cX) { return toupper(cX); }
template<class T> bool chmin(T& a,T b) { if(a > b){a = b; return true;} return false; }
template<class T> bool chmax(T& a,T b) { if(a < b){a = b; return true;} return false; }
int popcnt(unsigned long long n) { int cnt = 0; for (int i = 0; i < 64; i++)if ((n >> i) & 1)cnt++; return cnt; }
int d_sum(ll n) { int ret = 0; while (n > 0) { ret += n % 10; n /= 10; }return ret; }
int d_cnt(ll n) { int ret = 0; while (n > 0) { ret++; n /= 10; }return ret; }
ll gcd(ll a, ll b) { if (b == 0)return a; return gcd(b, a%b); };
ll lcm(ll a, ll b) { ll g = gcd(a, b); return a / g*b; };
ll MOD(ll x, ll m){return (x%m+m)%m; }
ll FLOOR(ll x, ll m) {ll r = (x%m+m)%m; return (x-r)/m; }
template<class T> using dijk = priority_queue<T, vector<T>, greater<T>>;
# define all(qpqpq)           (qpqpq).begin(),(qpqpq).end()
# define UNIQUE(wpwpw)        (wpwpw).erase(unique(all((wpwpw))),(wpwpw).end())
# define LOWER(epepe)         transform(all((epepe)),(epepe).begin(),TL<char>)
# define UPPER(rprpr)         transform(all((rprpr)),(rprpr).begin(),TU<char>)
# define rep(i,upupu)         for(ll i = 0, i##_len = (upupu);(i) < (i##_len);(i)++)
# define reps(i,opopo)        for(ll i = 1, i##_len = (opopo);(i) <= (i##_len);(i)++)
# define len(x)                ((ll)(x).size())
# define bit(n)               (1LL << (n))
# define pb push_back
# define eb emplace_back
# define exists(c, e)         ((c).find(e) != (c).end())

struct INIT{
	INIT(){
		std::ios::sync_with_stdio(false);
		std::cin.tie(0);
		cout << fixed << setprecision(20);
	}
}INIT;

namespace mmrz {
	void solve();
}

int main(){
	mmrz::solve();
}
#define debug(...) (static_cast<void>(0))

int SOLVE(){
	int n, m;
	cin >> n >> m;
	if(n==0) return 1;
	vector<int> a(n), b(m);
	rep(i,n) cin >> a[i];
	rep(j,m) cin >> b[j];

	map<char, int> ans;
	rep(i,n) {
		rep(j,m) {
			ll nm = a[i]*b[j];
			string str = to_string(nm);
			for(auto c: str) ans[c]++;
		}
	}

	rep(i,10) {
		char c = '0' + i;
		cout << ans[c] << ((i+1==10)?'\n':' ');
	}
	return 0;
}

void mmrz::solve(){
	while(!SOLVE());
}
accuracy: 1
time_ms: 80
memory_kb: 3416
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;

int main() {
    int n, m;
    while (true) {
        cin >> n >> m;
        if (n == 0 && m == 0) break;
        vector<int> a(n), b(m);
        for (int i = 0; i < n; i++) cin >> a[i];
        for (int i = 0; i < m; i++) cin >> b[i];
        vector<int> count(10, 0);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                string product = to_string(a[i] * b[j]);
                for (char digit : product) {
                    count[digit - '0']++;
                }
            }
        }
        for (int i = 0; i <= 9; i++) {
            cout << count[i];
            if (i < 9)
                cout << ' ';
        }
        cout << "\n";
    }
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3504
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;

int main() {
    int n, m;
    while (true) {
        cin >> n >> m;
        if (n == 0 && m == 0) break;
        vector<int> a(n), b(m);
        for (int i = 0; i < n; i++) cin >> a[i];
        for (int i = 0; i < m; i++) cin >> b[i];
        vector<int> count(10, 0);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                string product = to_string(a[i] * b[j]);
                for (char digit : product) {
                    count[digit - '0']++;
                }
            }
        }
        cout << count[0];
        for (int i = 1; i <= 9; i++) {
            cout << ' ' << count[i];
        }
        cout << "\n";
    }
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3500
final_rank: 7

code_snippet: #include<bits/stdc++.h>
// #include<atcoder/all>
// #include<boost/multiprecision/cpp_int.hpp>

using namespace std;
// using namespace atcoder;
// using bint = boost::multiprecision::cpp_int;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using P = pair<ll,ll>;
using vi = vector<ll>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using ve = vector<vector<int>>;
using vb = vector<bool>;
using vvb = vector<vb>;
#define rep(i,n) for(ll i = 0;i < (ll)n;i++)
#define ALL(x) (x).begin(),(x).end()
#define sz(c) ((ll)(c).size())
#define LB(A,x) (int)(lower_bound(A.begin(),A.end(),x)-A.begin())
#define UB(A,x) (int)(upper_bound(A.begin(),A.end(),x)-A.begin())
// #define MOD 1000000007
#define MOD 998244353
template<typename T>using min_priority_queue=priority_queue<T,vector<T>,greater<T>>;
template<typename T>ostream&operator<<(ostream&os,vector<T>&v){for(int i = 0;i < v.size();i++)os<<v[i]<<(i+1!=v.size()?" ":"");return os;}
template<typename T>istream&operator>>(istream&is,vector<T>&v){for(T&in:v)is>>in;return is;}
template<typename T1,typename T2>ostream&operator<<(ostream&os,pair<T1,T2>&p){os<<p.first<<" "<<p.second;return os;}
template<typename T1,typename T2>istream&operator>>(istream&is,pair<T1,T2>&p){is>>p.first>>p.second;return is;}
template<typename T> inline bool chmax(T &a,T b){if(a < b){a = b;return true;}return false;}
template<typename T> inline bool chmin(T &a,T b){if(a > b){a = b;return true;}return false;}
ld dist(ld x1,ld y1,ld x2, ld y2){return sqrtl((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));}



int main(){
  
  ios_base::sync_with_stdio(0), cin.tie(0);
  while(1){
    int n,m;cin >> n >> m;
    if(!n)break;
    vi a(n),b(m);cin >> a >> b;
    vi cnt(10);
    rep(i,n)rep(j,m){
      string s = to_string(a[i]*b[j]);
      rep(k,sz(s))cnt[s[k]-'0']++;
    }
    cout << cnt << "\n";
  }
  

  return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3544
final_rank: 17

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef vector<ll> vi;
typedef vector<vi> vvi;
typedef vector<vvi> vvvi;
typedef vector<bool> vb;
typedef vector<vb> vvb;
typedef vector<vvb> vvvb;
typedef vector<vvvb> vvvvb;
typedef pair<ll,ll> pi;
typedef pair<ll,pi> ppi;
#define FOR(i,l,r) for(ll i=l;i<r;i++)
#define REP(i,n) FOR(i,0,n)
#define RFOR(i,l,r) for(ll i=r-1;i>=l;i--)
#define RREP(i,n) RFOR(i,0,n)
#define sz(A) (ll)(A.size())
#define ALL(A) A.begin(),A.end()
#define LB(A,x) (ll)(lower_bound(ALL(A),x)-A.begin())
#define UB(A,x) (ll)(upper_bound(ALL(A),x)-A.begin())
#define COU(A,x) (UB(A,x)-LB(A,x))
#define F first
#define S second
template<typename T>using min_priority_queue=priority_queue<T,vector<T>,greater<T>>;
template<typename T1,typename T2>ostream&operator<<(ostream&os,pair<T1,T2>&p){os<<p.F<<" "<<p.S;return os;}
template<typename T1,typename T2>istream&operator>>(istream&is,pair<T1,T2>&p){is>>p.F>>p.S;return is;}
template<typename T>ostream&operator<<(ostream&os,vector<T>&v){REP(i,sz(v))os<<v[i]<<(i+1!=sz(v)?" ":"");return os;}
template<typename T>istream&operator>>(istream&is,vector<T>&v){for(T&in:v)is>>in;return is;}
template<class T>bool chmax(T&a,T b){if(a<b){a=b;return 1;}return 0;}
template<class T>bool chmin(T&a,T b){if(b<a){a=b;return 1;}return 0;}
const ll mod=998244353;
template<const long long int mod=998244353>
struct modint{
  using mint=modint<mod>;
  long long int x;
  modint(long long int _x=0):x(_x%mod){if(x<0)x+=mod;}
  long long int val(){return x;}
  mint&operator=(const mint&a){x=a.x;return *this;}
  mint&operator+=(const mint&a){x+=a.x;if(x>=mod)x-=mod;return *this;}
  mint&operator-=(const mint&a){x-=a.x;if(x<0)x+=mod;return *this;}
  mint&operator*=(const mint&a){x*=a.x;x%=mod;return *this;}
  friend mint operator+(const mint&a,const mint&b){return mint(a)+=b;}
  friend mint operator-(const mint&a,const mint&b){return mint(a)-=b;}
  friend mint operator*(const mint&a,const mint&b){return mint(a)*=b;}
  mint operator-()const{return mint(0)-*this;}
  mint pow(long long int n){
    if(!n)return 1;
    mint a=1;
    mint _x=x;
    while(n){
      if(n&1)a*=_x;
      _x=_x*_x;n>>=1;
    }
    return a;
  }
  mint inv(){return pow(mod-2);}
  mint&operator/=(mint&a){return *this*=a.inv();}
  friend mint operator/(const mint&a,mint b){return mint(a)/=b;}
};
using mint=modint<998244353>;
int main(){
    while(1){
        int N,M;cin>>N>>M;
        if(!N)return 0;
        vi A(N),B(M);cin>>A>>B;
        vi C(10);
        REP(i,N)REP(j,M){
            string S=to_string(A[i]*B[j]);
            for(auto c:S)C[c-'0']++;
        }
        cout<<C<<endl;
    }
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3508
final_rank: 10

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define SIZE 105


void calc(int num,int *ans){
    if(num==0){
        ans[0]++;
        return;
    }
    while(num>0){
        ans[num%10]++;
        num/=10;
    }
}


int main(){
    int n,m;
    int a[SIZE],b[SIZE];
    int ans[10];

    while(true){
        cin>>n>>m;
        if(n==0&&m==0)break;

        for(int i=0;i<10;i++){
            ans[i]=0;
        }
        for(int i=0;i<n;i++){
            cin>>a[i];
        }
        for(int i=0;i<m;i++){
            cin>>b[i];
        }

        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                calc(a[i]*b[j],ans);
            }
        }
        cout<<ans[0]<<" ";
        for(int i=1;i<9;i++){
            cout<<ans[i]<<" ";
        }
        cout<<ans[9]<<endl;
    }
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3100
final_rank: 1

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll = long long int;
typedef vector<ll> vll;
typedef vector<vll> vvll;
typedef vector<vvll> vvvll;
typedef vector<bool> vb;
typedef vector<string> vs;
typedef pair<ll,ll> pll;
typedef vector<pair<ll,ll>> vp;
#define rep(i,a,n) for(ll i=a;i<n;i++)
#define rrep(i,a,n) for(ll i=n-1;i>=a;i--)
#define mod 998244353
#define all(a) a.begin(),a.end()

int main(){
    while(true){
        ll n,m;
        cin >> n >> m;
        if(n == 0 && m == 0)return 0;
        vll a(n),b(m);
        rep(i,0,n)cin >> a[i];
        rep(i,0,m)cin >> b[i];
        string x;
        rep(i,0,n)rep(j,0,m){
            ll p = a[i]*b[j];
            x += to_string(p);
        }
        sort(all(x));
        rep(i,0,10){
            char q = '0'+i;
            cout << count(all(x),q);
            if(i != 9)cout << " ";
        }
        cout << endl;
    }
}
accuracy: 1
time_ms: 130
memory_kb: 3516
final_rank: 20

code_snippet: #include <bits/stdc++.h>
//#include "atcoder/all"
using namespace std;
//using namespace atcoder;
using ll = long long;
using ld = long double;
using uint = unsigned int;
using ull = unsigned long long;
#define all(x) (x).begin(), (x).end()
#define rep1(a)          for(ll i = 0; i < a; i++)
#define rep2(i, a)       for(ll i = 0; i < a; i++)
#define rep3(i, a, b)    for(ll i = a; i < b; i++)
#define rep4(i, a, b, c) for(ll i = a; i < b; i += c)
#define overload4(a, b, c, d, e, ...) e
#define rep(...) overload4(__VA_ARGS__, rep4, rep3, rep2, rep1)(__VA_ARGS__)
#define rrep(i, a, b) for (ll i = a; i >= b; i--)
#define fore(i, a) for (auto &i : a)
#define vvl(a,b,c) vector<vector<ll>>(a,vector<ll>(b,c))
#define vvvl(a,b,c,d) vector<vector<vector<ll>>>(a,vvl(b,c,d))
#define enld endl
#define fix(n) cout << fixed << setprecision(n);
#define yesno(b) cout << (b ? "Yes":"No") << endl
#define debug(var)  do{std::cout << #var << " : ";view(var);}while(0)
template<typename T> void view(T e){std::cout << e << std::endl;}
template<typename T> void view(const std::vector<T>& v){for(const auto& e : v){ std::cout << e << " "; } std::cout << std::endl;}
template<typename T> void view(const std::vector<std::vector<T> >& vv){ for(const auto& v : vv){ view(v); } }
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a = b; return 1; } return 0; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a = b; return 1; } return 0; }
template<class T>using rp_queue=priority_queue<T,vector<T>,greater<T>>;
void fast_io(){cin.tie(nullptr);ios_base::sync_with_stdio(false);}
const int inf = INT_MAX / 2;
const ll infl = 1LL<<60;
const ll mod = 1000000007;
const ll mod2 = 998244353;
const int dx[8] = {0, 1, 0,-1, 1, 1,-1,-1};
const int dy[8] = {1, 0,-1, 0, 1,-1, 1,-1};

int main() {
    fast_io();
    for(;;) {
        int H,W; cin >> H >> W;
        if(H==0 && W==0) break;
        vector<int>h(H),w(W);
        rep(H) cin >> h[i];
        rep(W) cin >> w[i];
        vector<int>ans(10,0);
        rep(i,H) rep(j,W) {
            int num = h[i] * w[j];
            fore(s,to_string(num)) ans[(s-'0')]++;
        }
        rep(i,10) {
            cout << ans[i];
            if(i!=9) cout << " ";
        }
        cout << endl;
    }
}
accuracy: 1
time_ms: 40
memory_kb: 3512
final_rank: 11

code_snippet: #pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2,fma,popcnt,bmi,bmi2")
#include <bits/stdc++.h>
using namespace std;
#if __has_include(<atcoder/all>)
    #include <atcoder/all>   // ac-library  #### CAREFUL on CodeForces
    using namespace atcoder; // ac-library
    std::ostream &operator<<(std::ostream &os, const modint998244353 &a) {return os<<a.val();}
    std::ostream &operator<<(std::ostream &os, const modint1000000007 &a) {return os<<a.val();}
#endif
// # Type Aliases
using ll = long long;
using ull = unsigned long long;
using ld = long double;
template<class T> using V = vector<T>;
template<class T, class U> using P = pair<T,U>;
template<class T> using max_heap = priority_queue<T>;
template<class T> using min_heap = priority_queue<T,vector<T>,greater<T>>;
// # Repeat Macros
#define OVERLOADER(_1,_2,_3,NAME,...) NAME
#define REP2(i,a,b) for (ll i = (a); i < (ll)(b); ++i)
#define REP(i,n) REP2(i,0,n)
#define rep(...) OVERLOADER(__VA_ARGS__,REP2,REP)(__VA_ARGS__)
#define repd(i,a,b) for (ll i = (ll)(b)-1; i >= (ll)(a); --i)
// # Abbreviation Macros
#define pb push_back
#define eb emplace_back
#define ALL(a) begin(a),end(a)
#define RALL(a) a.rbegin(),a.rend()
// # Fast IO and IO Settings
struct IO_Setting{IO_Setting(){cin.tie(nullptr);ios_base::sync_with_stdio(false);cout<<fixed<<setprecision(15);cerr<<fixed<<setprecision(3);}}io_setting;
// # Input Overloads
template<class T,class U> istream& operator>>(istream& is,P<T,U>&p){return is>>p.first>>p.second;}
template<class T> istream& operator>>(istream& is,V<T>&v){for(T& e:v) is>>e; return is;}
// # Output Overloads
template<class T,class U> ostream& operator<<(ostream& os,const P<T,U>& p){return os<<p.first<<' '<<p.second;}
template<class T> ostream& operator<<(ostream& os,const V<T>& v) {int ss=v.size(); rep(ii,ss) {os<<v[ii]<<(ii+1==ss?"":" ");} return os;}
// # Function Definition
template<class T> T sq(T x) { return x*x; }
template<class T, class U> inline bool chmin(T &a, U b) { if(a>b) {a=b; return 1;} return 0;}
template<class T, class U> inline bool chmax(T &a, U b) { if(a<b) {a=b; return 1;} return 0;}
ll max (ll lhs, ll rhs) {return std::max<ll>(lhs, rhs);}
ll min (ll lhs, ll rhs) {return std::min<ll>(lhs, rhs);}
template<class Container> Container reversed(const Container& v) {return Container(v.rbegin(), v.rend());}
template<class Container> Container sorted(const Container& v, bool rev=false) {Container r = v; rev ? sort(r.rbegin(), r.rend()) : sort(r.begin(), r.end()); return r;}
template<class T>void uniqify(V<T>& v){v.erase(unique(ALL(v)),v.end());}
template<class T>V<T> compress(V<T>& v){V<T>vals=v;sort(ALL(vals));uniqify(vals);rep(i,v.size())v[i]=lower_bound(ALL(vals),v[i])-vals.begin();return vals;}
template<class T>V<T> sum_array(const V<T>& v) {int n=v.size(); V<T> sum(n+1); rep(i,n) sum[i+1]=sum[i]+v[i]; return sum;}
template<class T>inline void drop(T&& x) { cout << x << endl; exit(0); }
inline void yesno(bool f) { cout << (f ? "Yes" : "No") << '\n'; }
// # Constant Definition
constexpr char ENDL = '\n';
constexpr ll INF = (1LL<<30)-1; // 問題毎にfit
constexpr ll INFLL = (1LL<<62)-1; // 問題毎にfit
const ld PI = acos(static_cast<long double>(-1));
constexpr int dy[]={0, 1, 0, -1};
constexpr int dx[]={1, 0, -1, 0};
// constexpr int dy[]={0, 1, 1, 1, 0, -1, -1, -1};
// constexpr int dx[]={1, 1, 0, -1, -1, -1, 0, 1};
// # Debug Macro
#ifdef LOCAL
    #include "debug_print.hpp"
    #define debug(...) debug_impl(__LINE__, __VA_ARGS__)
    #define debug_impl(...) debug_print::multi_print(#__VA_ARGS__, __VA_ARGS__)
#else
    #define debug(...) (static_cast<void>(0))
#endif
// using mint = modint1000000007;
// using mint = modint998244353;

ll solve() {
    ll n,m; cin>>n>>m;
    if(n == 0 && m == 0) return 0;
    V<ll> a(n); cin>>a;
    V<ll> b(m); cin>>b;
    V<ll> cnt(10);
    rep(i, n) {
        rep(j, m) {
            ll res = a[i] * b[j];
            string str = to_string(res);
            for(auto c : str) {
                cnt[c - '0']++;
            }
        }
    }
    cout << cnt << ENDL;
    return 1;
}

int main() {
    while(solve());
}
accuracy: 1
time_ms: 40
memory_kb: 3544
final_rank: 17

code_snippet: // C++ includes used for precompiling -*- C++ -*-
// Copyright (C) 2003-2013 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// <http://www.gnu.org/licenses/>.
/** @file stdc++.h
 *  This is an implementation file for a precompiled header.
 */
// 17.4.1.2 Headers
// C
#ifndef _GLIBCXX_NO_ASSERT
#include <cassert>
#endif
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <clocale>
#include <cmath>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#if __cplusplus >= 201103L
#include <ccomplex>
#include <cfenv>
#include <cinttypes>
#include <cstdbool>
#include <cstdint>
#include <ctgmath>
#include <cwchar>
#include <cwctype>
#endif
// C++
#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>
#if __cplusplus >= 201103L
#include <array>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <forward_list>
#include <future>
#include <initializer_list>
#include <mutex>
#include <random>
#include <ratio>
#include <regex>
#include <scoped_allocator>
#include <system_error>
#include <thread>
#include <tuple>
#include <typeindex>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#endif

using namespace std;
using ll = long long;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T &a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T &a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
constexpr int INF = 1000000000;
constexpr int64_t llINF = 3000000000000000000;
constexpr double eps = 1e-10;
const double pi = acos(-1);
struct linear_sieve {
    vector<int> least_factor, prime_list;
    linear_sieve(int n) : least_factor(n + 1, 0) {
        for (int i = 2; i <= n; i++) {
            if (least_factor[i] == 0) {
                least_factor[i] = i;
                prime_list.push_back(i);
            }
            for (int p : prime_list) {
                if (ll(i) * p > n || p > least_factor[i]) break;
                least_factor[i * p] = p;
            }
        }
    }
};
ll extgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = extgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
ll modpow(ll a, ll b, ll m) {
    ll res = 1;
    while (b) {
        if (b & 1) {
            res *= a;
            res %= m;
        }
        a *= a;
        a %= m;
        b >>= 1;
    }
    return res;
}
template <int modulo>
struct modint {
    int x;
    modint() : x(0) {}
    modint(int64_t y) : x(y >= 0 ? y % modulo : (modulo - (-y) % modulo) % modulo) {}
    modint &operator+=(const modint &p) {
        if ((x += p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint &operator-=(const modint &p) {
        if ((x += modulo - p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint &operator*=(const modint &p) {
        x = (int)(1LL * x * p.x % modulo);
        return *this;
    }
    modint &operator/=(const modint &p) {
        *this *= p.inv();
        return *this;
    }
    modint operator-() const { return modint(-x); }
    modint operator+(const modint &p) const { return modint(*this) += p; }
    modint operator-(const modint &p) const { return modint(*this) -= p; }
    modint operator*(const modint &p) const { return modint(*this) *= p; }
    modint operator/(const modint &p) const { return modint(*this) /= p; }
    bool operator==(const modint &p) const { return x == p.x; }
    bool operator!=(const modint &p) const { return x != p.x; }
    modint inv() const {
        int a = x, b = modulo, u = 1, v = 0, t;
        while (b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return modint(u);
    }
    modint pow(int64_t n) const {
        modint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }
    friend ostream &operator<<(ostream &os, const modint &p) { return os << p.x; }
    friend istream &operator>>(istream &is, modint &a) {
        int64_t t;
        is >> t;
        a = modint<modulo>(t);
        return (is);
    }
    int val() const { return x; }
    static constexpr int mod() { return modulo; }
    static constexpr int half() { return (modulo + 1) >> 1; }
};
template <typename T>
struct Binomial {
    vector<T> inv, fact, factinv;
    Binomial(int n) {
        inv.resize(n + 1);
        fact.resize(n + 1);
        factinv.resize(n + 1);
        inv[0] = fact[0] = factinv[0] = 1;
        for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
        factinv[n] = fact[n].inv();
        inv[n] = fact[n - 1] * factinv[n];
        for (int i = n - 1; i >= 1; i--) {
            factinv[i] = factinv[i + 1] * (i + 1);
            inv[i] = fact[i - 1] * factinv[i];
        }
    }
    T C(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r] * factinv[r];
    }
    T P(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r];
    }
    T H(int n, int r) {
        if (n == 0 && r == 0) return 1;
        if (n < 0 || r < 0) return 0;
        return r == 0 ? 1 : C(n + r - 1, r);
    }
};
template <class T>
struct Matrix {
    int n;
    vector<vector<T>> m;
    Matrix() : Matrix(0) {}
    Matrix(int x) : Matrix(vector<vector<T>>(x, vector<T>(x, 0))) {}
    Matrix(const vector<vector<T>> &a) {
        n = a.size();
        m = a;
    }
    vector<T> &operator[](int i) { return m[i]; }
    const vector<T> &operator[](int i) const { return m[i]; }
    static Matrix identity(int x) {
        Matrix res(x);
        for (int i = 0; i < x; i++) res[i][i] = 1;
        return res;
    }
    Matrix operator+(const Matrix &a) const {
        Matrix x = (*this);
        return x += a;
    }
    Matrix operator*(const Matrix &a) const {
        Matrix x = (*this);
        return x *= a;
    }
    Matrix &operator+=(const Matrix &a) {
        Matrix res(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                res[i][j] = m[i][j] + a[i][j];
            }
        }
        m = res.m;
        return *this;
    }
    Matrix &operator*=(const Matrix &a) {
        Matrix res(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                for (int k = 0; k < n; k++) {
                    res[i][j] += m[i][k] * a[k][j];
                }
            }
        }
        m = res.m;
        return *this;
    }
    Matrix pow(ll b) const {
        Matrix x = *this, res = identity(n);
        while (b) {
            if (b & 1) {
                res *= x;
            }
            x *= x;
            b >>= 1;
        }
        return res;
    }
};
template <class S, S (*op)(S, S), S (*e)()>
struct segtree {
    int siz = 1;
    vector<S> dat;
    segtree(int n) : segtree(vector<S>(n, e())) {}
    segtree(const vector<S> &a) {
        while (siz < a.size()) siz <<= 1;
        dat = vector<S>(siz << 1, e());
        for (int i = 0; i < a.size(); i++) dat[siz + i] = a[i];
        for (int i = siz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void set(int p, S x) {
        p += siz;
        dat[p] = x;
        while (p > 0) {
            p >>= 1;
            dat[p] = op(dat[2 * p], dat[2 * p + 1]);
        }
    }
    void add(int p, S x) { set(p, get(p) + x); }
    S get(int p) { return dat[p + siz]; }
    S prod(int l, int r) {
        S vl = e(), vr = e();
        l += siz, r += siz;
        while (l < r) {
            if (l & 1) vl = op(vl, dat[l++]);
            if (r & 1) vr = op(dat[--r], vr);
            l >>= 1, r >>= 1;
        }
        return op(vl, vr);
    }
    S all_prod() { return dat[1]; }
};

template <class T>
struct BIT {
    // 1-indexed
    int n, beki = 1;
    vector<T> bit;
    BIT(int x) {
        bit.resize(x + 1, 0);
        n = x;
        while (beki * 2 <= n) beki *= 2;
    }
    T sum(int i) {
        T res = 0;
        while (i > 0) {
            res += bit[i];
            i -= i & -i;
        }
        return res;
    }
    T sum(int l, int r) {
        //[l,r]
        return sum(r) - (l == 0 ? 0 : sum(l - 1));
    }
    void add(int i, T x) {
        while (i <= n) {
            bit[i] += x;
            i += i & -i;
        }
    }
    int lowerbound(T w) {
        if (w <= 0) return 0;
        int x = 0;
        for (int k = beki; k > 0; k >>= 1) {
            if (x + k <= n && bit[x + k] < w) {
                w -= bit[x + k];
                x += k;
            }
        }
        return x + 1;
    }
};
template <class T, T (*op)(T, T), T (*e)()>
struct sparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    sparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            for (int j = 0; j + (1 << i) <= (1 << len); j++) {
                table[i][j] = op(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
            }
        }
        logtable.resize(v.size() + 1);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        assert(l <= r);
        if (l == r) return e();
        int len = logtable[r - l];
        return op(table[len][l], table[len][r - (1 << len)]);
    }
};
template <class T, T (*op)(T, T), T (*e)()>
struct disjointsparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    disjointsparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len, e()));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            int shift = 1 << i;
            for (int j = 0; j < (int)v.size(); j += shift << 1) {
                int t = min(j + shift, (int)v.size());
                table[i][t - 1] = v[t - 1];
                for (int k = t - 2; k >= j; k--) table[i][k] = op(v[k], table[i][k + 1]);
                if (v.size() <= t) break;
                table[i][t] = v[t];
                int r = min(t + shift, (int)v.size());
                for (int k = t + 1; k < r; k++) table[i][k] = op(table[i][k - 1], v[k]);
            }
        }
        logtable.resize(1 << len);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        if (l == r) return e();
        if (l >= --r) return table[0][l];
        int len = logtable[l ^ r];
        return op(table[len][l], table[len][r]);
    };
};
struct lca_tree {
    int n, size;
    vector<vector<int>> par;
    vector<int> depth;
    lca_tree(vector<vector<int>> g, int root = 0) : n((int)g.size()), size(log2(n) + 2), par(size, vector<int>(n, -1)), depth(vector<int>(n, n)) {
        queue<int> que;
        depth[root] = 0;
        que.push(root);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (int i : g[p]) {
                if (depth[i] > depth[p] + 1) {
                    depth[i] = depth[p] + 1;
                    par[0][i] = p;
                    que.push(i);
                }
            }
        }
        for (int k = 0; k < size - 1; k++) {
            for (int i = 0; i < n; i++) {
                if (par[k][i] == -1)
                    par[k + 1][i] = -1;
                else
                    par[k + 1][i] = par[k][par[k][i]];
            }
        }
    }
    int query(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        for (int k = size - 1; k >= 0; k--) {
            if (((depth[v] - depth[u]) >> k) & 1) v = par[k][v];
            if (u == v) return u;
        }
        for (int k = size - 1; k >= 0; k--) {
            if (par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }
    int dist(int u, int v) {
        int l = query(u, v);
        return depth[u] + depth[v] - 2 * depth[l];
    }
};
struct UnionFind {
    vector<int> par, siz, es;
    UnionFind(int x) {
        par.resize(x);
        siz.resize(x);
        es.resize(x);
        for (int i = 0; i < x; i++) {
            par[i] = i;
            siz[i] = 1;
            es[i] = 0;
        }
    }
    int find(int x) {
        if (par[x] == x) return x;
        return par[x] = find(par[x]);
    }
    bool unite(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) {
            es[x]++;
            return false;
        }
        if (siz[x] < siz[y]) swap(x, y);
        par[y] = x;
        siz[x] += siz[y];
        es[x] += es[y] + 1;
        return true;
    }
    bool same(int x, int y) { return find(x) == find(y); }
    int size(int x) { return siz[find(x)]; }
    int edges(int x) { return es[find(x)]; }
};
struct Mo {
    int n;
    vector<pair<int, int>> lr;

    explicit Mo(int n) : n(n) {}

    void add(int l, int r) { /* [l, r) */ lr.emplace_back(l, r); }

    template <typename AL, typename AR, typename EL, typename ER, typename O>
    void build(const AL &add_left, const AR &add_right, const EL &erase_left, const ER &erase_right, const O &out) {
        int q = (int)lr.size();
        int bs = n / min<int>(n, sqrt(q));
        vector<int> ord(q);
        iota(begin(ord), end(ord), 0);
        sort(begin(ord), end(ord), [&](int a, int b) {
            int ablock = lr[a].first / bs, bblock = lr[b].first / bs;
            if (ablock != bblock) return ablock < bblock;
            return (ablock & 1) ? lr[a].second > lr[b].second : lr[a].second < lr[b].second;
        });
        int l = 0, r = 0;
        for (auto idx : ord) {
            while (l > lr[idx].first) add_left(--l);
            while (r < lr[idx].second) add_right(r++);
            while (l < lr[idx].first) erase_left(l++);
            while (r > lr[idx].second) erase_right(--r);
            out(idx);
        }
    }

    template <typename A, typename E, typename O>
    void build(const A &add, const E &erase, const O &out) {
        build(add, add, erase, erase, out);
    }
};
template <class S, S (*op)(S, S), S (*e)(), class F, S (*mapping)(F, S), F (*composition)(F, F), F (*id)()>
struct lazy_segtree {
    int _n, sz = 1;
    vector<S> dat;
    vector<F> lazy;
    lazy_segtree(vector<S> a) : _n(int(a.size())) {
        while (sz < _n) sz <<= 1;
        dat.resize(sz * 2, e());
        lazy.resize(sz * 2, id());
        rep(i, _n) dat[sz + i] = a[i];
        for (int i = sz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void eval(int k) {
        dat[k] = mapping(lazy[k], dat[k]);
        if (k < sz) {
            lazy[k * 2] = composition(lazy[k], lazy[k * 2]);
            lazy[k * 2 + 1] = composition(lazy[k], lazy[k * 2 + 1]);
        }
        lazy[k] = id();
    }
    void apply(int a, int b, F f, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return;
        if (a <= l && r <= b) {
            lazy[k] = composition(f, lazy[k]);
            eval(k);
            return;
        }
        int m = (l + r) >> 1;
        apply(a, b, f, 2 * k, l, m);
        apply(a, b, f, 2 * k + 1, m, r);
        dat[k] = op(dat[2 * k], dat[2 * k + 1]);
    }
    S prod(int a, int b, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return e();
        if (a <= l && r <= b) return dat[k];
        int m = (l + r) >> 1;
        S vl = prod(a, b, 2 * k, l, m);
        S vr = prod(a, b, 2 * k + 1, m, r);
        return op(vl, vr);
    }
};

template <class S, S (*op)(S, S), S (*e)()>
struct dual_segtree {
    int sz = 1, log = 0;
    vector<S> lz;
    dual_segtree(vector<S> a) {
        int n = a.size();
        while (sz < n) {
            sz <<= 1;
            log++;
        }
        lz.assign(sz << 1, e());
        for (int i = 0; i < n; i++) lz[i + sz] = a[i];
    }
    void push(int k) {
        int b = __builtin_ctz(k);
        for (int d = log; d > b; d--) {
            lz[k >> d << 1] = op(lz[k >> d << 1], lz[k >> d]);
            lz[k >> d << 1 | 1] = op(lz[k >> d << 1 | 1], lz[k >> d]);
            lz[k >> d] = e();
        }
    }
    void apply(int l, int r, S x) {
        l += sz, r += sz;
        push(l);
        push(r);
        while (l < r) {
            if (l & 1) {
                lz[l] = op(lz[l], x);
                l++;
            }
            if (r & 1) {
                r--;
                lz[r] = op(lz[r], x);
            }
            l >>= 1, r >>= 1;
        }
    }
    S get(int k) {
        k += sz;
        S res = e();
        while (k) {
            res = op(res, lz[k]);
            k >>= 1;
        }
        return res;
    }
};
ll modinv(int x, int modulo) {
    int a = x, b = modulo, u = 1, v = 0, t;
    while (b > 0) {
        t = a / b;
        swap(a -= t * b, b);
        swap(u -= t * v, v);
    }
    return (u >= 0 ? u % modulo : (modulo - (-u) % modulo) % modulo);
}
struct auxiliary_tree : lca_tree {
    vector<int> in;
    vector<vector<int>> G;
    auxiliary_tree(vector<vector<int>> &g) : lca_tree(g), in((int)g.size()), G((int)g.size()) {
        int t = 0;
        dfs(0, -1, t, g);
    }
    void dfs(int v, int p, int &t, vector<vector<int>> &g) {
        in[v] = t++;
        for (auto u : g[v]) {
            if (u != p) dfs(u, v, t, g);
        }
    }
    using lca_tree::depth;
    pair<int, vector<int>> query(vector<int> vs, bool decending = false) {
        // decending: only parent to child
        assert(!vs.empty());
        sort(vs.begin(), vs.end(), [&](int a, int b) { return in[a] < in[b]; });
        int m = vs.size();
        stack<int> st;
        st.push(vs[0]);
        for (int i = 0; i < m - 1; i++) {
            int w = lca_tree::query(vs[i], vs[i + 1]);
            if (w != vs[i]) {
                int l = st.top();
                st.pop();
                while (!st.empty() && depth[w] < depth[st.top()]) {
                    if (!decending) G[l].push_back(st.top());
                    G[st.top()].push_back(l);
                    l = st.top();
                    st.pop();
                }
                if (st.empty() || st.top() != w) {
                    st.push(w);
                    vs.push_back(w);
                }
                if (!decending) G[l].push_back(w);
                G[w].push_back(l);
            }
            st.push(vs[i + 1]);
        }
        while (st.size() > 1) {
            int x = st.top();
            st.pop();
            if (!decending) G[x].push_back(st.top());
            G[st.top()].push_back(x);
        }
        // {root,vertex_list}
        return make_pair(st.top(), vs);
    }
    void clear(vector<int> vs) {
        for (int v : vs) G[v].clear();
    }
};
struct LowLink {
    vector<vector<int>> g;
    vector<int> ord, low, out;
    vector<bool> used;
    vector<pair<int, int>> bridge;
    vector<pair<int, int>> articulation;
    int unions;
    LowLink(vector<vector<int>> g) : g(g) {
        int n = (int)g.size();
        ord.resize(n);
        low.resize(n);
        out.resize(n);
        used.resize(n);
        unions = 0;
        int t = 0;
        for (int i = 0; i < n; i++) {
            if (!used[i]) {
                dfs(i, t, -1);
                unions++;
            }
        }
    }
    void dfs(int v, int &t, int par) {
        used[v] = true;
        ord[v] = t++, low[v] = ord[v];
        int cnt = 0;
        bool par_back = false;
        for (int to : g[v]) {
            if (!used[to]) {
                dfs(to, t, v);
                low[v] = min(low[v], low[to]);
                if (ord[v] < low[to]) bridge.push_back(minmax(v, to));
                if (ord[v] <= low[to]) cnt++;
            } else if (to != par || par_back) {
                low[v] = min(low[v], ord[to]);
            } else
                par_back = true;
        }
        if (par != -1) cnt++;
        if (cnt >= 2) articulation.push_back({v, cnt});
        out[v] = t;
    }
};
void solve(int n,int m) {
    vector<int>a(n),b(m);
    rep(i,n)cin>>a[i];
    rep(i,m)cin>>b[i];
    vector<int>cnt(10);
    rep(i,n){
        rep(j,m){
            string s=to_string(a[i]*b[j]);
            for(char c:s){
                cnt[c-'0']++;
            }
        }
    }
    rep(i,10){
        cout<<cnt[i];
        if(i==9)cout<<endl;
        else cout<<" ";
    }
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int n,m;
    while(cin>>n>>m){
        if(n==0)break;
    solve(n,m);
    }
}
accuracy: 1
time_ms: 40
memory_kb: 3524
final_rank: 13

code_snippet: #include <bits/stdc++.h>
using namespace std;
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using ll = long long;
using ld = long double;
template <class T>
using vc = vector<T>;
template <class T>
using vvc = vc<vc<T>>;
void solve();
int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    ll t = 1;
    // cin >> t;
    for (int i = 1; i <= t; i++) solve();
    return 0;
}
#define rep(i, a, b) for (ll i = (a); i < (b); i++)
ll dy[4] = {0, 1, 0, -1}, dx[4] = {1, 0, -1, 0};
// ll dy[8] = {1, 1, 0, -1, -1, -1, 0, 1}, dx[8] = {0, 1, 1, 1, 0, -1, -1, -1};

void solve() {
    while (true) {
        ll n, m;
        cin >> n >> m;
        if (n == 0 && m == 0) break;
        vc<ll> a(n), b(m);
        rep(i, 0, n) cin >> a[i];
        rep(i, 0, m) cin >> b[i];
        vc<ll> cnt(10);
        rep(i, 0, n) rep(j, 0, m) {
            ll v = a[i] * b[j];
            string s = to_string(v);
            for (auto c : s) cnt[c - '0']++;
        }
        rep(i, 0, 10) cout << cnt[i] << " \n"[i == 9];
    }
}
accuracy: 1
time_ms: 40
memory_kb: 3524
final_rank: 13

code_snippet: #include<iostream>
#include<vector>
using namespace std;
int A[109],B[109];
bool solve()
{
	int N,M;cin>>N>>M;
	if(N==0)return false;
	vector<int>cnt(10,0);
	for(int i=0;i<N;i++)cin>>A[i];
	for(int i=0;i<M;i++)cin>>B[i];
	for(int i=0;i<N;i++)
	{
		for(int j=0;j<M;j++)
		{
			int c=A[i]*B[j];
			while(c>0)
			{
				cnt[c%10]++;
				c/=10;
			}
		}
	}
	for(int i=0;i<10;i++)
	{
		if(i>0)cout<<" ";
		cout<<cnt[i];
	}
	cout<<endl;
	return true;
}
int main()
{
	while(solve());
}
accuracy: 1
time_ms: 10
memory_kb: 3104
final_rank: 2

code_snippet: #include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define all(x) (x).begin(),(x).end()
#define eb emplace_back
using namespace std;
using ll = long long;
using ld = long double;
using vl = vector<long long>;
using vvl = vector<vector<long long>>; using vs = vector<string>;
using pl = pair<long long, long long>;
template <typename T> inline bool chmin(T& a, const T& b) {bool compare = a > b; if (a > b) a = b; return compare;}
template <typename T> inline bool chmax(T& a, const T& b) {bool compare = a < b; if (a < b) a = b; return compare;}
template<class T>using rp_queue=priority_queue<T,vector<T>,greater<T>>;
template <typename T> T gcd(T a, T b) {if (b == 0)return a; else return gcd(b, a % b);}
template <typename T> inline T lcm(T a, T b) {return a /gcd(a, b)*b;}
const ll INF = 1LL << 60;
const ld PI = acos(-1);
template <class OStream, class T> OStream &operator<<(OStream &os, const std::vector<T> &vec);
template <class OStream, class T, size_t sz> OStream &operator<<(OStream &os, const std::array<T, sz> &arr);
template <class OStream, class T, class TH> OStream &operator<<(OStream &os, const std::unordered_set<T, TH> &vec);
template <class OStream, class T, class U> OStream &operator<<(OStream &os, const pair<T, U> &pa);
template <class OStream, class T> OStream &operator<<(OStream &os, const std::deque<T> &vec);
template <class OStream, class T> OStream &operator<<(OStream &os, const std::set<T> &vec);
template <class OStream, class T> OStream &operator<<(OStream &os, const std::multiset<T> &vec);
template <class OStream, class T> OStream &operator<<(OStream &os, const std::unordered_multiset<T> &vec);
template <class OStream, class T, class U> OStream &operator<<(OStream &os, const std::pair<T, U> &pa);
template <class OStream, class TK, class TV> OStream &operator<<(OStream &os, const std::map<TK, TV> &mp);
template <class OStream, class TK, class TV, class TH> OStream &operator<<(OStream &os, const std::unordered_map<TK, TV, TH> &mp);
template <class OStream, class... T> OStream &operator<<(OStream &os, const std::tuple<T...> &tpl);
 
template <class OStream, class T> OStream &operator<<(OStream &os, const std::vector<T> &vec) { os << '['; for (auto v : vec) os << v << ','; os << ']'; return os; }
template <class OStream, class T, size_t sz> OStream &operator<<(OStream &os, const std::array<T, sz> &arr) { os << '['; for (auto v : arr) os << v << ','; os << ']'; return os; }
template <class... T> std::istream &operator>>(std::istream &is, std::tuple<T...> &tpl) { std::apply([&is](auto &&... args) { ((is >> args), ...);}, tpl); return is; }
template <class OStream, class... T> OStream &operator<<(OStream &os, const std::tuple<T...> &tpl) { os << '('; std::apply([&os](auto &&... args) { ((os << args << ','), ...);}, tpl); return os << ')'; }
template <class OStream, class T, class TH> OStream &operator<<(OStream &os, const std::unordered_set<T, TH> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <class OStream, class T> OStream &operator<<(OStream &os, const std::deque<T> &vec) { os << "deq["; for (auto v : vec) os << v << ','; os << ']'; return os; }
template <class OStream, class T> OStream &operator<<(OStream &os, const std::set<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <class OStream, class T> OStream &operator<<(OStream &os, const std::multiset<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <class OStream, class T> OStream &operator<<(OStream &os, const std::unordered_multiset<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <class OStream, class T, class U> OStream &operator<<(OStream &os, const std::pair<T, U> &pa) { return os << '(' << pa.first << ',' << pa.second << ')'; }
template <class OStream, class TK, class TV> OStream &operator<<(OStream &os, const std::map<TK, TV> &mp) { os << '{'; for (auto v : mp) os << v.first << "=>" << v.second << ','; os << '}'; return os; }
template <class OStream, class TK, class TV, class TH> OStream &operator<<(OStream &os, const std::unordered_map<TK, TV, TH> &mp) { os << '{'; for (auto v : mp) os << v.first << "=>" << v.second << ','; os << '}'; return os; }
#ifdef LOCAL
const string COLOR_RESET = "\033[0m", BRIGHT_GREEN = "\033[1;32m", BRIGHT_RED = "\033[1;31m", BRIGHT_CYAN = "\033[1;36m", NORMAL_CROSSED = "\033[0;9;37m", RED_BACKGROUND = "\033[1;41m", NORMAL_FAINT = "\033[0;2m";
#define dbg(x) std::cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " << __FILE__ << COLOR_RESET << std::endl
#define dbgif(cond, x) ((cond) ? std::cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " << __FILE__ << COLOR_RESET << std::endl : std::cerr)
#else
#define dbg(x) ((void)0)
#define dbgif(cond, x) ((void)0)
#endif
void solve();
int main(){
  cin.tie(nullptr);
  ios_base::sync_with_stdio(false);
  cout<<fixed<<setprecision(15);
  int num_tc = 1;
  //cin >> num_tc;
  rep(tc,num_tc){
    //cout << "Case #" << tc+1 << ": " ;// << endl;
    solve();
  }
}
//見切り発車で実装しては行けない．頭の中でコードが完全に出来上がるまで書き始めるな．
void solve(){
  while(true){
    int N,M;cin>>N>>M;
    if(N==0&&M==0)return;
    vl cnt(10);
    vl A(N);rep(i,N)cin>>A[i];
    vl B(M);rep(i,M)cin>>B[i];
    rep(i,N){
      rep(j,M){
        for(auto a:to_string(A[i]*B[j])){
          cnt[a-'0']++;
        }
      }
    }
    rep(i,10){
      cout<<cnt[i];
      if(i==9)cout<<endl;
      else cout<<" ";
    }
  }
}
accuracy: 1
time_ms: 40
memory_kb: 3512
final_rank: 11

code_snippet: // C++ includes used for precompiling -*- C++ -*-
// Copyright (C) 2003-2013 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// <http://www.gnu.org/licenses/>.
/** @file stdc++.h
 *  This is an implementation file for a precompiled header.
 */
// 17.4.1.2 Headers
// C
#ifndef _GLIBCXX_NO_ASSERT
#include <cassert>
#endif
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <clocale>
#include <cmath>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#if __cplusplus >= 201103L
#include <ccomplex>
#include <cfenv>
#include <cinttypes>
#include <cstdbool>
#include <cstdint>
#include <ctgmath>
#include <cwchar>
#include <cwctype>
#endif
// C++
#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>
#if __cplusplus >= 201103L
#include <array>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <forward_list>
#include <future>
#include <initializer_list>
#include <mutex>
#include <random>
#include <ratio>
#include <regex>
#include <scoped_allocator>
#include <system_error>
#include <thread>
#include <tuple>
#include <typeindex>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#endif

using namespace std;
using ll = long long;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T &a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T &a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
constexpr int INF = 1000000000;
constexpr int64_t llINF = 3000000000000000000;
constexpr double eps = 1e-10;
const double pi = acos(-1);
struct linear_sieve {
    vector<int> least_factor, prime_list;
    linear_sieve(int n) : least_factor(n + 1, 0) {
        for (int i = 2; i <= n; i++) {
            if (least_factor[i] == 0) {
                least_factor[i] = i;
                prime_list.push_back(i);
            }
            for (int p : prime_list) {
                if (ll(i) * p > n || p > least_factor[i]) break;
                least_factor[i * p] = p;
            }
        }
    }
};
ll extgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = extgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
ll modpow(ll a, ll b, ll m) {
    ll res = 1;
    while (b) {
        if (b & 1) {
            res *= a;
            res %= m;
        }
        a *= a;
        a %= m;
        b >>= 1;
    }
    return res;
}
template <int modulo>
struct modint {
    int x;
    modint() : x(0) {}
    modint(int64_t y) : x(y >= 0 ? y % modulo : (modulo - (-y) % modulo) % modulo) {}
    modint &operator+=(const modint &p) {
        if ((x += p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint &operator-=(const modint &p) {
        if ((x += modulo - p.x) >= modulo) x -= modulo;
        return *this;
    }
    modint &operator*=(const modint &p) {
        x = (int)(1LL * x * p.x % modulo);
        return *this;
    }
    modint &operator/=(const modint &p) {
        *this *= p.inv();
        return *this;
    }
    modint operator-() const { return modint(-x); }
    modint operator+(const modint &p) const { return modint(*this) += p; }
    modint operator-(const modint &p) const { return modint(*this) -= p; }
    modint operator*(const modint &p) const { return modint(*this) *= p; }
    modint operator/(const modint &p) const { return modint(*this) /= p; }
    bool operator==(const modint &p) const { return x == p.x; }
    bool operator!=(const modint &p) const { return x != p.x; }
    modint inv() const {
        int a = x, b = modulo, u = 1, v = 0, t;
        while (b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return modint(u);
    }
    modint pow(int64_t n) const {
        modint ret(1), mul(x);
        while (n > 0) {
            if (n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }
    friend ostream &operator<<(ostream &os, const modint &p) { return os << p.x; }
    friend istream &operator>>(istream &is, modint &a) {
        int64_t t;
        is >> t;
        a = modint<modulo>(t);
        return (is);
    }
    int val() const { return x; }
    static constexpr int mod() { return modulo; }
    static constexpr int half() { return (modulo + 1) >> 1; }
};
template <typename T>
struct Binomial {
    vector<T> inv, fact, factinv;
    Binomial(int n) {
        inv.resize(n + 1);
        fact.resize(n + 1);
        factinv.resize(n + 1);
        inv[0] = fact[0] = factinv[0] = 1;
        for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
        factinv[n] = fact[n].inv();
        inv[n] = fact[n - 1] * factinv[n];
        for (int i = n - 1; i >= 1; i--) {
            factinv[i] = factinv[i + 1] * (i + 1);
            inv[i] = fact[i - 1] * factinv[i];
        }
    }
    T C(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r] * factinv[r];
    }
    T P(int n, int r) {
        if (n < 0 || n < r || r < 0) return 0;
        return fact[n] * factinv[n - r];
    }
    T H(int n, int r) {
        if (n == 0 && r == 0) return 1;
        if (n < 0 || r < 0) return 0;
        return r == 0 ? 1 : C(n + r - 1, r);
    }
};
template <class T>
struct Matrix {
    int n;
    vector<vector<T>> m;
    Matrix() : Matrix(0) {}
    Matrix(int x) : Matrix(vector<vector<T>>(x, vector<T>(x, 0))) {}
    Matrix(const vector<vector<T>> &a) {
        n = a.size();
        m = a;
    }
    vector<T> &operator[](int i) { return m[i]; }
    const vector<T> &operator[](int i) const { return m[i]; }
    static Matrix identity(int x) {
        Matrix res(x);
        for (int i = 0; i < x; i++) res[i][i] = 1;
        return res;
    }
    Matrix operator+(const Matrix &a) const {
        Matrix x = (*this);
        return x += a;
    }
    Matrix operator*(const Matrix &a) const {
        Matrix x = (*this);
        return x *= a;
    }
    Matrix &operator+=(const Matrix &a) {
        Matrix res(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                res[i][j] = m[i][j] + a[i][j];
            }
        }
        m = res.m;
        return *this;
    }
    Matrix &operator*=(const Matrix &a) {
        Matrix res(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                for (int k = 0; k < n; k++) {
                    res[i][j] += m[i][k] * a[k][j];
                }
            }
        }
        m = res.m;
        return *this;
    }
    Matrix pow(ll b) const {
        Matrix x = *this, res = identity(n);
        while (b) {
            if (b & 1) {
                res *= x;
            }
            x *= x;
            b >>= 1;
        }
        return res;
    }
};
template <class S, S (*op)(S, S), S (*e)()>
struct segtree {
    int siz = 1;
    vector<S> dat;
    segtree(int n) : segtree(vector<S>(n, e())) {}
    segtree(const vector<S> &a) {
        while (siz < a.size()) siz <<= 1;
        dat = vector<S>(siz << 1, e());
        for (int i = 0; i < a.size(); i++) dat[siz + i] = a[i];
        for (int i = siz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void set(int p, S x) {
        p += siz;
        dat[p] = x;
        while (p > 0) {
            p >>= 1;
            dat[p] = op(dat[2 * p], dat[2 * p + 1]);
        }
    }
    void add(int p, S x) { set(p, get(p) + x); }
    S get(int p) { return dat[p + siz]; }
    S prod(int l, int r) {
        S vl = e(), vr = e();
        l += siz, r += siz;
        while (l < r) {
            if (l & 1) vl = op(vl, dat[l++]);
            if (r & 1) vr = op(dat[--r], vr);
            l >>= 1, r >>= 1;
        }
        return op(vl, vr);
    }
    S all_prod() { return dat[1]; }
};

template <class T>
struct BIT {
    // 1-indexed
    int n, beki = 1;
    vector<T> bit;
    BIT(int x) {
        bit.resize(x + 1, 0);
        n = x;
        while (beki * 2 <= n) beki *= 2;
    }
    T sum(int i) {
        T res = 0;
        while (i > 0) {
            res += bit[i];
            i -= i & -i;
        }
        return res;
    }
    T sum(int l, int r) {
        //[l,r]
        return sum(r) - (l == 0 ? 0 : sum(l - 1));
    }
    void add(int i, T x) {
        while (i <= n) {
            bit[i] += x;
            i += i & -i;
        }
    }
    int lowerbound(T w) {
        if (w <= 0) return 0;
        int x = 0;
        for (int k = beki; k > 0; k >>= 1) {
            if (x + k <= n && bit[x + k] < w) {
                w -= bit[x + k];
                x += k;
            }
        }
        return x + 1;
    }
};
template <class T, T (*op)(T, T), T (*e)()>
struct sparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    sparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            for (int j = 0; j + (1 << i) <= (1 << len); j++) {
                table[i][j] = op(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
            }
        }
        logtable.resize(v.size() + 1);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        assert(l <= r);
        if (l == r) return e();
        int len = logtable[r - l];
        return op(table[len][l], table[len][r - (1 << len)]);
    }
};
template <class T, T (*op)(T, T), T (*e)()>
struct disjointsparsetable {
    vector<vector<T>> table;
    vector<int> logtable;
    disjointsparsetable(vector<T> v) {
        int len = 0;
        while ((1 << len) <= v.size()) len++;
        table.assign(len, vector<T>(1 << len, e()));
        for (int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for (int i = 1; i < len; i++) {
            int shift = 1 << i;
            for (int j = 0; j < (int)v.size(); j += shift << 1) {
                int t = min(j + shift, (int)v.size());
                table[i][t - 1] = v[t - 1];
                for (int k = t - 2; k >= j; k--) table[i][k] = op(v[k], table[i][k + 1]);
                if (v.size() <= t) break;
                table[i][t] = v[t];
                int r = min(t + shift, (int)v.size());
                for (int k = t + 1; k < r; k++) table[i][k] = op(table[i][k - 1], v[k]);
            }
        }
        logtable.resize(1 << len);
        for (int i = 2; i < logtable.size(); i++) {
            logtable[i] = logtable[(i >> 1)] + 1;
        }
    }
    T query(int l, int r) {
        if (l == r) return e();
        if (l >= --r) return table[0][l];
        int len = logtable[l ^ r];
        return op(table[len][l], table[len][r]);
    };
};
struct lca_tree {
    int n, size;
    vector<vector<int>> par;
    vector<int> depth;
    lca_tree(vector<vector<int>> g, int root = 0) : n((int)g.size()), size(log2(n) + 2), par(size, vector<int>(n, -1)), depth(vector<int>(n, n)) {
        queue<int> que;
        depth[root] = 0;
        que.push(root);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (int i : g[p]) {
                if (depth[i] > depth[p] + 1) {
                    depth[i] = depth[p] + 1;
                    par[0][i] = p;
                    que.push(i);
                }
            }
        }
        for (int k = 0; k < size - 1; k++) {
            for (int i = 0; i < n; i++) {
                if (par[k][i] == -1)
                    par[k + 1][i] = -1;
                else
                    par[k + 1][i] = par[k][par[k][i]];
            }
        }
    }
    int query(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        for (int k = size - 1; k >= 0; k--) {
            if (((depth[v] - depth[u]) >> k) & 1) v = par[k][v];
            if (u == v) return u;
        }
        for (int k = size - 1; k >= 0; k--) {
            if (par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }
    int dist(int u, int v) {
        int l = query(u, v);
        return depth[u] + depth[v] - 2 * depth[l];
    }
};
struct UnionFind {
    vector<int> par, siz, es;
    UnionFind(int x) {
        par.resize(x);
        siz.resize(x);
        es.resize(x);
        for (int i = 0; i < x; i++) {
            par[i] = i;
            siz[i] = 1;
            es[i] = 0;
        }
    }
    int find(int x) {
        if (par[x] == x) return x;
        return par[x] = find(par[x]);
    }
    bool unite(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) {
            es[x]++;
            return false;
        }
        if (siz[x] < siz[y]) swap(x, y);
        par[y] = x;
        siz[x] += siz[y];
        es[x] += es[y] + 1;
        return true;
    }
    bool same(int x, int y) { return find(x) == find(y); }
    int size(int x) { return siz[find(x)]; }
    int edges(int x) { return es[find(x)]; }
};
struct Mo {
    int n;
    vector<pair<int, int>> lr;

    explicit Mo(int n) : n(n) {}

    void add(int l, int r) { /* [l, r) */ lr.emplace_back(l, r); }

    template <typename AL, typename AR, typename EL, typename ER, typename O>
    void build(const AL &add_left, const AR &add_right, const EL &erase_left, const ER &erase_right, const O &out) {
        int q = (int)lr.size();
        int bs = n / min<int>(n, sqrt(q));
        vector<int> ord(q);
        iota(begin(ord), end(ord), 0);
        sort(begin(ord), end(ord), [&](int a, int b) {
            int ablock = lr[a].first / bs, bblock = lr[b].first / bs;
            if (ablock != bblock) return ablock < bblock;
            return (ablock & 1) ? lr[a].second > lr[b].second : lr[a].second < lr[b].second;
        });
        int l = 0, r = 0;
        for (auto idx : ord) {
            while (l > lr[idx].first) add_left(--l);
            while (r < lr[idx].second) add_right(r++);
            while (l < lr[idx].first) erase_left(l++);
            while (r > lr[idx].second) erase_right(--r);
            out(idx);
        }
    }

    template <typename A, typename E, typename O>
    void build(const A &add, const E &erase, const O &out) {
        build(add, add, erase, erase, out);
    }
};
template <class S, S (*op)(S, S), S (*e)(), class F, S (*mapping)(F, S), F (*composition)(F, F), F (*id)()>
struct lazy_segtree {
    int _n, sz = 1;
    vector<S> dat;
    vector<F> lazy;
    lazy_segtree(vector<S> a) : _n(int(a.size())) {
        while (sz < _n) sz <<= 1;
        dat.resize(sz * 2, e());
        lazy.resize(sz * 2, id());
        rep(i, _n) dat[sz + i] = a[i];
        for (int i = sz - 1; i >= 1; i--) dat[i] = op(dat[2 * i], dat[2 * i + 1]);
    }
    void eval(int k) {
        dat[k] = mapping(lazy[k], dat[k]);
        if (k < sz) {
            lazy[k * 2] = composition(lazy[k], lazy[k * 2]);
            lazy[k * 2 + 1] = composition(lazy[k], lazy[k * 2 + 1]);
        }
        lazy[k] = id();
    }
    void apply(int a, int b, F f, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return;
        if (a <= l && r <= b) {
            lazy[k] = composition(f, lazy[k]);
            eval(k);
            return;
        }
        int m = (l + r) >> 1;
        apply(a, b, f, 2 * k, l, m);
        apply(a, b, f, 2 * k + 1, m, r);
        dat[k] = op(dat[2 * k], dat[2 * k + 1]);
    }
    S prod(int a, int b, int k = 1, int l = 0, int r = -1) {
        eval(k);
        if (r == -1) r = sz;
        if (r <= a || b <= l) return e();
        if (a <= l && r <= b) return dat[k];
        int m = (l + r) >> 1;
        S vl = prod(a, b, 2 * k, l, m);
        S vr = prod(a, b, 2 * k + 1, m, r);
        return op(vl, vr);
    }
};

template <class S, S (*op)(S, S), S (*e)()>
struct dual_segtree {
    int sz = 1, log = 0;
    vector<S> lz;
    dual_segtree(vector<S> a) {
        int n = a.size();
        while (sz < n) {
            sz <<= 1;
            log++;
        }
        lz.assign(sz << 1, e());
        for (int i = 0; i < n; i++) lz[i + sz] = a[i];
    }
    void push(int k) {
        int b = __builtin_ctz(k);
        for (int d = log; d > b; d--) {
            lz[k >> d << 1] = op(lz[k >> d << 1], lz[k >> d]);
            lz[k >> d << 1 | 1] = op(lz[k >> d << 1 | 1], lz[k >> d]);
            lz[k >> d] = e();
        }
    }
    void apply(int l, int r, S x) {
        l += sz, r += sz;
        push(l);
        push(r);
        while (l < r) {
            if (l & 1) {
                lz[l] = op(lz[l], x);
                l++;
            }
            if (r & 1) {
                r--;
                lz[r] = op(lz[r], x);
            }
            l >>= 1, r >>= 1;
        }
    }
    S get(int k) {
        k += sz;
        S res = e();
        while (k) {
            res = op(res, lz[k]);
            k >>= 1;
        }
        return res;
    }
};
ll modinv(int x, int modulo) {
    int a = x, b = modulo, u = 1, v = 0, t;
    while (b > 0) {
        t = a / b;
        swap(a -= t * b, b);
        swap(u -= t * v, v);
    }
    return (u >= 0 ? u % modulo : (modulo - (-u) % modulo) % modulo);
}
struct auxiliary_tree : lca_tree {
    vector<int> in;
    vector<vector<int>> G;
    auxiliary_tree(vector<vector<int>> &g) : lca_tree(g), in((int)g.size()), G((int)g.size()) {
        int t = 0;
        dfs(0, -1, t, g);
    }
    void dfs(int v, int p, int &t, vector<vector<int>> &g) {
        in[v] = t++;
        for (auto u : g[v]) {
            if (u != p) dfs(u, v, t, g);
        }
    }
    using lca_tree::depth;
    pair<int, vector<int>> query(vector<int> vs, bool decending = false) {
        // decending: only parent to child
        assert(!vs.empty());
        sort(vs.begin(), vs.end(), [&](int a, int b) { return in[a] < in[b]; });
        int m = vs.size();
        stack<int> st;
        st.push(vs[0]);
        for (int i = 0; i < m - 1; i++) {
            int w = lca_tree::query(vs[i], vs[i + 1]);
            if (w != vs[i]) {
                int l = st.top();
                st.pop();
                while (!st.empty() && depth[w] < depth[st.top()]) {
                    if (!decending) G[l].push_back(st.top());
                    G[st.top()].push_back(l);
                    l = st.top();
                    st.pop();
                }
                if (st.empty() || st.top() != w) {
                    st.push(w);
                    vs.push_back(w);
                }
                if (!decending) G[l].push_back(w);
                G[w].push_back(l);
            }
            st.push(vs[i + 1]);
        }
        while (st.size() > 1) {
            int x = st.top();
            st.pop();
            if (!decending) G[x].push_back(st.top());
            G[st.top()].push_back(x);
        }
        // {root,vertex_list}
        return make_pair(st.top(), vs);
    }
    void clear(vector<int> vs) {
        for (int v : vs) G[v].clear();
    }
};
struct LowLink {
    vector<vector<int>> g;
    vector<int> ord, low, out;
    vector<bool> used;
    vector<pair<int, int>> bridge;
    vector<pair<int, int>> articulation;
    int unions;
    LowLink(vector<vector<int>> g) : g(g) {
        int n = (int)g.size();
        ord.resize(n);
        low.resize(n);
        out.resize(n);
        used.resize(n);
        unions = 0;
        int t = 0;
        for (int i = 0; i < n; i++) {
            if (!used[i]) {
                dfs(i, t, -1);
                unions++;
            }
        }
    }
    void dfs(int v, int &t, int par) {
        used[v] = true;
        ord[v] = t++, low[v] = ord[v];
        int cnt = 0;
        bool par_back = false;
        for (int to : g[v]) {
            if (!used[to]) {
                dfs(to, t, v);
                low[v] = min(low[v], low[to]);
                if (ord[v] < low[to]) bridge.push_back(minmax(v, to));
                if (ord[v] <= low[to]) cnt++;
            } else if (to != par || par_back) {
                low[v] = min(low[v], ord[to]);
            } else
                par_back = true;
        }
        if (par != -1) cnt++;
        if (cnt >= 2) articulation.push_back({v, cnt});
        out[v] = t;
    }
};
void solve(int n,int m) {
    vector<int>a(n),b(m);
    rep(i,n)cin>>a[i];
    rep(i,m)cin>>b[i];
    vector<int>cnt(10);
    rep(i,n){
        rep(j,m){
            string s=to_string(a[i]*b[j]);
            for(char c:s){
                cnt[c-'0']++;
            }
        }
    }
    rep(i,10){
        cout<<cnt[i];
        if(i==9)cout<<endl;
        else cout<<" ";
    }
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int n,m;
    while(cin>>n>>m){
        if(n==0)break;
    solve(n,m);
    }
}
accuracy: 1
time_ms: 40
memory_kb: 3328
final_rank: 5

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define rep(s,i,n) for(int i=s;i<n;i++)
#define rrep(s,i,n) for(int i=n-1;i>=s;i--)
#define C(n) cout<<n<<endl;
#define cd(d) cout<<setprecision(20)<<d<<endl;
#define cv(v) rep(0,i,v.size())C(v[i])
#define ic(n) int n;cin>>n;
#define sc(s) string s;cin>>s;
#define dc(d) double d;cin>>d;
#define vi vector<int>
#define vic(a) rep(0,i,a.size())cin>>a[i];
#define vii(a,n,m) vector<vector<int>> a(n,vector<int>(m));
#define vm vector<mint>
#define vmm(a,n,m) vector<vector<mint>> a(n,vector<mint>(m));
#define vd vector<double>
#define vdd(a,n,m) vector<vector<double>> a(n,vector<double>(m));
#define vc vector<char>
#define vcc(a,n,m) vector<vector<char>> a(n,vector<char>(m));
#define vs vector<string>
#define vss(a,n,m) vector<vector<string>> a(n,vector<string>(m));
#define vb vector<bool>
#define vbb(a,n,m) vector<vector<bool>> a(n,vector<bool>(m));
#define vp vector<pair<int,int>>
#define CY c("Yes") return 0;
#define CN c("No") return 0;
#define YN(x) if(x)C("Yes") else C("No")
#define all(a) a.begin(),a.end()
#define mod 998244353
#define mod2 1000000007
#define inf 2000000000000000000
#define f first
#define s second
#define P pair<int,int>
#define pi 3.141592653589793238462643383279
#define pb push_back
int max(int x,int y){
    if(x>=y)return x;
    else return y;
}
int min(int x,int y){
    if(x<=y)return x;
    else return y;
}
int gcd(int x,int y){
    x=abs(x),y=abs(y);
    if(x==0||y==0)return x+y;
    if(x<y)swap(x,y);
    return gcd(x%y,y);
}
int lcm(int x,int y){
    x=abs(x),y=abs(y);
    return x/gcd(x,y)*y;
}
int pow(int n,int m,int modulo){
    int pro=1;
    int nn=n;
    while(m){
        if(m%2==1)pro=pro*nn%modulo;
        m=m/2;
        nn=nn*nn%modulo;
    }
    return pro;
}
int inv(int n,int m,int modulo){
    int t=pow(m,modulo-2,modulo);
    return n*t%modulo;
}
template<class T> inline bool chmin(T& a, T b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template<class T> inline bool chmax(T& a, T b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template<int MOD> struct Fp {
    long long val;
    constexpr Fp(long long v = 0) noexcept : val(v % MOD) {
        if (val < 0) v += MOD;
    }
    constexpr int getmod() { return MOD; }
    constexpr Fp operator - () const noexcept {
        return val ? MOD - val : 0;
    }
    constexpr Fp operator + (const Fp& r) const noexcept { return Fp(*this) += r; }
    constexpr Fp operator - (const Fp& r) const noexcept { return Fp(*this) -= r; }
    constexpr Fp operator * (const Fp& r) const noexcept { return Fp(*this) *= r; }
    constexpr Fp operator / (const Fp& r) const noexcept { return Fp(*this) /= r; }
    constexpr Fp& operator += (const Fp& r) noexcept {
        val += r.val;
        if (val >= MOD) val -= MOD;
        return *this;
    }
    constexpr Fp& operator -= (const Fp& r) noexcept {
        val -= r.val;
        if (val < 0) val += MOD;
        return *this;
    }
    constexpr Fp& operator *= (const Fp& r) noexcept {
        val = val * r.val % MOD;
        return *this;
    }
    constexpr Fp& operator /= (const Fp& r) noexcept {
        long long a = r.val, b = MOD, u = 1, v = 0;
        while (b) {
            long long t = a / b;
            a -= t * b; swap(a, b);
            u -= t * v; swap(u, v);
        }
        val = val * u % MOD;
        if (val < 0) val += MOD;
        return *this;
    }
    constexpr bool operator == (const Fp& r) const noexcept {
        return this->val == r.val;
    }
    constexpr bool operator != (const Fp& r) const noexcept {
        return this->val != r.val;
    }
    friend constexpr ostream& operator << (ostream &os, const Fp<MOD>& x) noexcept {
        return os << x.val;
    }
    friend constexpr istream& operator >> (istream &is, Fp<MOD>& x) noexcept {
        return is >> x.val;
    }
    friend constexpr Fp<MOD> modpow(const Fp<MOD> &a, long long n) noexcept {
        if (n == 0) return 1;
        auto t = modpow(a, n / 2);
        t = t * t;
        if (n & 1) t = t * a;
        return t;
    }
};
using mint=Fp<mod>;
using mint2=Fp<mod2>;
struct comb{
    vector<mint> fac,invfac;
    comb(int n){
        fac.pb(1);
        rep(1,i,n+1)fac.pb(fac[i-1]*i);
        rep(0,i,n+1){
            invfac.pb(1);
            invfac[i]/=fac[i];
        }
    }
    mint com(int n,int m){
        if(n<0||m<0)return 0;
        if(n<m)return 0;
        else return fac[n]*invfac[m]*invfac[n-m];
    }
};
using Graph=vector<vector<int>>;
Graph makeG(int n,int m,vector<int> u,vector<int> v,bool d){
    Graph G(n);
    rep(0,i,m){
        G[u[i]].pb(v[i]);
        if(d)G[v[i]].pb(u[i]);
    }
    return G;
}
struct UF{
    vector<int> par,rank,siz;
    UF(int n):par(n,-1),rank(n,0),siz(n,1){}
    int root(int x){
        if(par[x]==-1)return x;
        else return par[x]=root(par[x]);
    }
    bool  same(int x,int y){
        return root(x)==root(y);
    }
    bool unite(int x,int y){
        int rx=root(x),ry=root(y);
        if(rx==ry)return false;
        if(rank[rx]<rank[ry])swap(rx,ry);
        par[ry]=rx;
        if(rank[rx]==rank[ry])rank[rx]++;
        siz[rx]+=siz[ry];
        return true;
    }
    int size(int x){
        return siz[root(x)];
    }
};
Graph makeUF(int n,Graph g){
    UF d(n);
    vector<vector<int>> m(n);
    rep(0,i,n){
        for(auto t:g[i])d.unite(i,t);
    }
    rep(0,i,n)m[d.root(i)].pb(i);
    return m;
}
using wegraph=vector<vector<P>>;
wegraph makeWG(int n,int m,vector<int> u,vector<int> v,vector<int> c,bool d){
    wegraph G(n);
    rep(0,i,m){
        G[u[i]].pb({v[i],c[i]});
        if(d)G[v[i]].pb({u[i],c[i]});
    }
    return G;
}
vector<int> dijkstra(int n,wegraph g,int st){
    vector<int> dist(n,inf);
    priority_queue<P,vector<P>,greater<P>> pq;
    dist[st]=0;
    pq.push({0,st});
    while(!pq.empty()){
        P p=pq.top();pq.pop();
        int v=p.s;
        if(dist[v]<p.f)continue;
        for(auto e:g[v]){
            if(dist[e.f]>dist[v]+e.s){
                dist[e.f]=dist[v]+e.s;
                pq.push({dist[e.f],e.f});
            }
        }
    }
    return dist;
}
vector<int> toposo(int n,vector<vector<int>> G){
    vector<int> indegree(n);
    rep(0,i,n){
        for(auto g:G[i]){
            indegree[g]++;
        }
    }
	vector<int> sorted_vertices;
	priority_queue<int,vector<int>,greater<int>> que;
	rep(0,i,n)if(!indegree[i])que.push(i);
	while(!que.empty()){
		int v=que.top();
		que.pop();
		rep(0,i,G[v].size()){
			int u=G[v][i];
			indegree[u]-=1;
			if(!indegree[u])que.push(u);
		}
		sorted_vertices.pb(v);
	}
	return sorted_vertices;
}
template< typename T >
struct SparseTable {
  vector< vector< T > > st;
  vector< int > lookup;

  SparseTable(const vector< T > &v) {
    int b = 0;
    while((1 << b) <= v.size()) ++b;
    st.assign(b, vector< T >(1 << b));
    for(int i = 0; i < v.size(); i++) {
      st[0][i] = v[i];
    }
    for(int i = 1; i < b; i++) {
      for(int j = 0; j + (1 << i) <= (1 << b); j++) {
        st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
      }
    }
    lookup.resize(v.size() + 1);
    for(int i = 2; i < lookup.size(); i++) {
      lookup[i] = lookup[i >> 1] + 1;
    }
  }

  inline T rmq(int l, int r) {
    int b = lookup[r - l];
    return min(st[b][l], st[b][r - (1 << b)]);
  }
};
struct Mo {
  int n;
  vector< pair< int, int > > lr;
 
  explicit Mo(int n) : n(n) {}
 
  void add(int l, int r) { /* [l, r) */
    lr.emplace_back(l, r);
  }
 
  template< typename AL, typename AR, typename EL, typename ER, typename O >
  void build(const AL &add_left, const AR &add_right, const EL &erase_left, const ER &erase_right, const O &out) {
    int q = (int) lr.size();
    int bs = n / min< int >(n, sqrt(q));
    vector< int > ord(q);
    iota(begin(ord), end(ord), 0);
    sort(begin(ord), end(ord), [&](int a, int b) {
      int ablock = lr[a].first / bs, bblock = lr[b].first / bs;
      if(ablock != bblock) return ablock < bblock;
      return (ablock & 1) ? lr[a].second > lr[b].second : lr[a].second < lr[b].second;
    });
    int l = 0, r = 0;
    for(auto idx : ord) {
      while(l > lr[idx].first) add_left(--l);
      while(r < lr[idx].second) add_right(r++);
      while(l < lr[idx].first) erase_left(l++);
      while(r > lr[idx].second) erase_right(--r);
      out(idx);
    }
  }
 
  template< typename A, typename E, typename O >
  void build(const A &add, const E &erase, const O &out) {
    build(add, add, erase, erase, out);
  }
};
template<typename T,T INF>
struct Beats {
    int size = 1;
 
   private:
    vector<T> mx, smx, mxc;
    vector<T> mn, smn, mnc;
    vector<T> sum, lazy;
    vector<bool> flag;
 
    void update(int k) {
        sum[k] = sum[k * 2 + 1] + sum[k * 2 + 2];
 
        mx[k] = max(mx[2 * k + 1], mx[2 * k + 2]);
        if (mx[2 * k + 1] < mx[2 * k + 2]) {
            mxc[k] = mxc[2 * k + 2];
            smx[k] = max(mx[2 * k + 1], smx[2 * k + 2]);
        } else if (mx[2 * k + 1] > mx[2 * k + 2]) {
            mxc[k] = mxc[2 * k + 1];
            smx[k] = max(smx[2 * k + 1], mx[2 * k + 2]);
        } else {
            mxc[k] = mxc[2 * k + 1] + mxc[2 * k + 2];
            smx[k] = max(smx[2 * k + 1], smx[2 * k + 2]);
        }
 
        mn[k] = min(mn[2 * k + 1], mn[2 * k + 2]);
        if (mn[2 * k + 1] < mn[2 * k + 2]) {
            mnc[k] = mnc[2 * k + 1];
            smn[k] = min(smn[2 * k + 1], mn[2 * k + 2]);
        } else if (mn[2 * k + 1] > mn[2 * k + 2]) {
            mnc[k] = mnc[2 * k + 2];
            smn[k] = min(mn[2 * k + 1], smn[2 * k + 2]);
        } else {
            mnc[k] = mnc[2 * k + 1] + mnc[2 * k + 2];
            smn[k] = min(smn[2 * k + 1], smn[2 * k + 2]);
        }
    }
    void updateNodeMax(int k, T x) {
        sum[k] += (x - mx[k]) * mxc[k];
        if (mx[k] == mn[k]) {
            mx[k] = mn[k] = x;
        } else if (mx[k] == smn[k]) {
            mx[k] = smn[k] = x;
        } else {
            mx[k] = x;
        }
    }
    void updateNodeMin(int k, T x) {
        sum[k] += (x - mn[k]) * mnc[k];
        if (mx[k] == mn[k]) {
            mx[k] = mn[k] = x;
        } else if (smx[k] == mn[k]) {
            smx[k] = mn[k] = x;
        } else {
            mn[k] = x;
        }
    }
    void updateNodeAdd(int k, int len, T x) {
        mx[k] += x;
        if (smx[k] != -INF) smx[k] += x;
        mn[k] += x;
        if (smn[k] != INF) smn[k] += x;
        sum[k] += x * len;
        lazy[k] += x;
    }
    void updateNodeAssign(int k, int len, T x) {
        mx[k] = x;
        smx[k] = -INF;
        mxc[k] = len;
        mn[k] = x;
        smn[k] = INF;
        mnc[k] = len;
        sum[k] = x * len;
        lazy[k] = x;
        flag[k] = true;
    }
    void push(int k, int len) {
        if (k >= size - 1) return;
        if (flag[k] || lazy[k] != 0) {
            if (flag[k]) {
                updateNodeAssign(k * 2 + 1, len / 2, lazy[k]);
                updateNodeAssign(k * 2 + 2, len / 2, lazy[k]);
            } else {
                updateNodeAdd(k * 2 + 1, len / 2, lazy[k]);
                updateNodeAdd(k * 2 + 2, len / 2, lazy[k]);
            }
            lazy[k] = 0;
            flag[k] = false;
        }
        if (mx[k * 2 + 1] > mx[k]) updateNodeMax(k * 2 + 1, mx[k]);
        if (mx[k * 2 + 2] > mx[k]) updateNodeMax(k * 2 + 2, mx[k]);
        if (mn[k * 2 + 1] < mn[k]) updateNodeMin(k * 2 + 1, mn[k]);
        if (mn[k * 2 + 2] < mn[k]) updateNodeMin(k * 2 + 2, mn[k]);
    }
 
   public:
    void updateMin(int a, int b, T x, int k = 0, int l = 0, int r = -1) {
        if (r == -1) r = size;
        if (r <= a || b <= l || mx[k] <= x) return;
        if (a <= l && r <= b && smx[k] < x) {
            updateNodeMax(k, x);
            return;
        }
        push(k, r - l);
        updateMin(a, b, x, k * 2 + 1, l, (l + r) / 2);
        updateMin(a, b, x, k * 2 + 2, (l + r) / 2, r);
        update(k);
    }
    void updateMax(int a, int b, T x, int k = 0, int l = 0, int r = -1) {
        if (r == -1) r = size;
        if (r <= a || b <= l || mn[k] >= x) return;
        if (a <= l && r <= b && smn[k] > x) {
            updateNodeMin(k, x);
            return;
        }
        push(k, r - l);
        updateMax(a, b, x, k * 2 + 1, l, (l + r) / 2);
        updateMax(a, b, x, k * 2 + 2, (l + r) / 2, r);
        update(k);
    }
    void updateAdd(int a, int b, T x, int k = 0, int l = 0, int r = -1) {
        if (r == -1) r = size;
        if (r <= a || b <= l) return;
        if (a <= l && r <= b) {
            updateNodeAdd(k, r - l, x);
            return;
        }
        push(k, r - l);
        updateAdd(a, b, x, k * 2 + 1, l, (l + r) / 2);
        updateAdd(a, b, x, k * 2 + 2, (l + r) / 2, r);
        update(k);
    }
    void updateAssign(int a, int b, T x, int k = 0, int l = 0, int r = -1) {
        if (r == -1) r = size;
        if (r <= a || b <= l) return;
        if (a <= l && r <= b) {
            updateNodeAssign(k, r - l, x);
            return;
        }
        push(k, r - l);
        updateAssign(a, b, x, k * 2 + 1, l, (l + r) / 2);
        updateAssign(a, b, x, k * 2 + 2, (l + r) / 2, r);
        update(k);
    }
 
    void set(int k, T x) {
        k += size - 1;
        mx[k] = x;
        mn[k] = x;
        sum[k] = x;
    }
    void init() {
        for (T i = size - 2; i >= 0; i--) update(i);
    }
    T queryMin(int a, int b, int k = 0, int l = 0, int r = -1) {
        if (r == -1) r = size;
        if (r <= a || b <= l) return INF;
        if (a <= l && r <= b) return mn[k];
        push(k, r - l);
        T lv = queryMin(a, b, k * 2 + 1, l, (l + r) / 2);
        T rv = queryMin(a, b, k * 2 + 2, (l + r) / 2, r);
        return min(lv, rv);
    }
    T queryMax(int a, int b, int k = 0, int l = 0, int r = -1) {
        if (r == -1) r = size;
        if (r <= a || b <= l) return -INF;
        if (a <= l && r <= b) return mx[k];
        push(k, r - l);
        T lv = queryMax(a, b, k * 2 + 1, l, (l + r) / 2);
        T rv = queryMax(a, b, k * 2 + 2, (l + r) / 2, r);
        return max(lv, rv);
    }
    T querySum(int a, int b, int k = 0, int l = 0, int r = -1) {
        if (r == -1) r = size;
        if (r <= a || b <= l) return 0;
        if (a <= l && r <= b) return sum[k];
        push(k, r - l);
        T lv = querySum(a, b, k * 2 + 1, l, (l + r) / 2);
        T rv = querySum(a, b, k * 2 + 2, (l + r) / 2, r);
        return lv + rv;
    }
    Beats(int x) {
        while (size < x) size *= 2;
        mx.resize(size * 2 - 1, -INF + 1);
        smx.resize(size * 2 - 1, -INF);
        mxc.resize(size * 2 - 1, 1);
        mn.resize(size * 2 - 1, INF - 1);
        smn.resize(size * 2 - 1, INF);
        mnc.resize(size * 2 - 1, 1);
        
        sum.resize(size * 2 - 1);
        lazy.resize(size * 2 - 1);
        flag.resize(size * 2 - 1);
    }
};
struct DFS{
	Graph G;
	vector<int> par,dep,subt;
	vector<vector<int>> subz;
	int r,n;
	DFS(int N,Graph a,int root):G(N),par(N),dep(N),subt(N),subz(N){
		n=N;
		G=a;
		r=root;
	}
	void dfs2(int v,int p,int d){
		dep[v]=d;
		par[v]=p;
		for(auto nv:G[v]){
			if(nv==p)continue;
			dfs2(nv,v,d+1);
		}
		subt[v]=1;
		for(auto c:G[v]){
			if(c==p)continue;
			subt[v]+=subt[c];
		}
	}
	void dfs(){
		dfs2(r,-1,0);
		rep(0,u,n){
			for(auto v:G[u]){
				if(u<v){
					if(dep[u]>dep[v]){
						subz[u].pb(n-subt[u]);
						subz[v].pb(subt[u]);
					}
					else{
						subz[u].pb(subt[v]);
						subz[v].pb(n-subt[v]);
					}
				}
			}
		}
        dfs();
	}
    vector<int> parent(){
        return par;
    }
	vector<int> depth(){
		return dep;
	}
	vector<int> subtree(){
		return subt;
	}
	vector<vector<int>> subtr(){
		return subz;
	}
};
struct LCA{
    Graph G;
	vector<int> par,dep,subt;
	vector<vector<int>> subz,par2;
	int r,n;
    void dfs2(int v,int p,int d){
	    dep[v]=d;
		par[v]=p;
    	for(auto nv:G[v]){
	    	if(nv==p)continue;
		   	dfs2(nv,v,d+1);
		}
    	subt[v]=1;
	    for(auto c:G[v]){
		    if(c==p)continue;
			subt[v]+=subt[c];
		}
	}
	void dfs(){
		dfs2(r,-1,0);
		rep(0,u,n){
			for(auto v:G[u]){
				if(u<v){
					if(dep[u]>dep[v]){
						subz[u].pb(n-subt[u]);
						subz[v].pb(subt[u]);
				    }
					else{
						subz[u].pb(subt[v]);
						subz[v].pb(n-subt[v]);
					}
				}
		    }
        }
    }
	LCA(int N,Graph a,int root):G(N),par(N),par2(N),dep(N),subt(N),subz(N){
		n=N;
		G=a;
		r=root;
        dfs();
        rep(0,i,n){
            par2[i].pb(par[i]);
        }
        int k=1;
        while((1<<k)<n)k++;
        rep(0,i,k){
            rep(0,j,n){
                if(par2[j][i]==-1){
                    par2[j].pb(-1);
                }
                else{
                    par2[j].pb(par2[par2[j][i]][i]);
                }
            }
        }
	}
    int query(int u,int v){
        if(dep[u]<dep[v])swap(u,v);
        int k=par2[0].size();
        rep(0,i,k){
            if((dep[u]-dep[v])&(1<<i)){
                u=par2[u][i];
            }
        }
        if(u==v)return u;
        rrep(0,i,k){
            if(par2[u][i]!=par2[v][i]){
                u=par2[u][i];
                v=par2[v][i];
            }
        }
        return par2[u][0];
    }
    int dist(int u,int v){
        int x=query(u,v);
        return dep[u]+dep[v]-2*dep[x];
    }
    int parent(int i){
        return par[i];
    }
	int depth(int i){
		return dep[i];
	}
	vector<int> subtree(){
		return subt;
	}
	vector<vector<int>> subtr(){
		return subz;
	}

};
struct EulerTour{
    vector<int> ans,par;
    int root;
    int n;
    Graph G;
    void dfs(int v,int p){
        ans.pb(v);
        for(auto g:G[v]){
            if(g!=p){
                dfs(g,v);
                ans.pb(v);
            }
        }
    }
    EulerTour(int N,Graph g,int r):G(N){
        n=N;
        G=g;
        root=r;
    }
    vector<int> et(){
        dfs(root,-1);
        return ans;
    }
};
/* RMQ：[0,n-1] について、区間ごとの最小値を管理する構造体
    set(int i, T x), build(): i番目の要素をxにセット。まとめてセグ木を構築する。O(n)
    update(i,x): i 番目の要素を x に更新。O(log(n))
    query(a,b): [a,b) での最小の要素を取得。O(log(n))
    find_rightest(a,b,x): [a,b) で x以下の要素を持つ最右位置を求める。O(log(n))
    find_leftest(a,b,x): [a,b) で x以下の要素を持つ最左位置を求める。O(log(n))
*/
template <typename T>
struct RMQ {
    const T e = numeric_limits<T>::max();
    function<T(T, T)> fx = [](T x1, T x2) -> T { return min(x1, x2); };
    int n;
    vector<T> dat;
    RMQ(int n_) : n(), dat(n_ * 4, e) {
        int x = 1;
        while (n_ > x) {
            x *= 2;
        }
        n = x;
    }

    void set(int i, T x) { dat[i + n - 1] = x; }
    void build() {
        for (int k = n - 2; k >= 0; k--) dat[k] = fx(dat[2 * k + 1], dat[2 * k + 2]);
    }

    void update(int i, T x) {
        i += n - 1;
        dat[i] = x;
        while (i > 0) {
            i = (i - 1) / 2;  // parent
            dat[i] = fx(dat[i * 2 + 1], dat[i * 2 + 2]);
        }
    }

    // the minimum element of [a,b)
    T query(int a, int b) { return query_sub(a, b, 0, 0, n); }
    T query_sub(int a, int b, int k, int l, int r) {
        if (r <= a || b <= l) {
            return e;
        } else if (a <= l && r <= b) {
            return dat[k];
        } else {
            T vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2);
            T vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r);
            return fx(vl, vr);
        }
    }

    int find_rightest(int a, int b, T x) { return find_rightest_sub(a, b, x, 0, 0, n); }
    int find_leftest(int a, int b, T x) { return find_leftest_sub(a, b, x, 0, 0, n); }
    int find_rightest_sub(int a, int b, T x, int k, int l, int r) {
        if (dat[k] > x || r <= a || b <= l) {  // 自分の値がxより大きい or [a,b)が[l,r)の範囲外ならreturn a-1
            return a - 1;
        } else if (k >= n - 1) {  // 自分が葉ならその位置をreturn
            return (k - (n - 1));
        } else {
            int vr = find_rightest_sub(a, b, x, 2 * k + 2, (l + r) / 2, r);
            if (vr != a - 1) {  // 右の部分木を見て a-1 以外ならreturn
                return vr;
            } else {  // 左の部分木を見て値をreturn
                return find_rightest_sub(a, b, x, 2 * k + 1, l, (l + r) / 2);
            }
        }
    }
    int find_leftest_sub(int a, int b, T x, int k, int l, int r) {
        if (dat[k] > x || r <= a || b <= l) {  // 自分の値がxより大きい or [a,b)が[l,r)の範囲外ならreturn b
            return b;
        } else if (k >= n - 1) {  // 自分が葉ならその位置をreturn
            return (k - (n - 1));
        } else {
            int vl = find_leftest_sub(a, b, x, 2 * k + 1, l, (l + r) / 2);
            if (vl != b) {  // 左の部分木を見て b 以外ならreturn
                return vl;
            } else {  // 右の部分木を見て値をreturn
                return find_leftest_sub(a, b, x, 2 * k + 2, (l + r) / 2, r);
            }
        }
    }
};
void solve(int n,int m){
    vi a(n),b(m);
    vic(a);
    vic(b);
    vi ans(10);
    rep(0,i,n)rep(0,j,m){
        int x=a[i]*b[j];
        string s=to_string(x);
        rep(0,k,s.size())ans[s[k]-'0']++;
    }
    rep(0,i,10){
        cout<<ans[i];
        if(i!=9)cout<<" ";
    }
    cout<<endl;
}
signed main(){
    while(true){
        ic(n) ic(m)
        if(n==0&&m==0)return 0;
        solve(n,m);
    }
}
accuracy: 1
time_ms: 40
memory_kb: 3524
final_rank: 13

code_snippet: #include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize("Ofast")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
using ll=long long;

using ld=long double;

bool solve(){
    int n,m;cin>>n>>m;
    if(n==0 && m==0){
        return false;
    }
    vector<int>a(n);
    vector<int>b(m);
    for(auto &x:a)cin>>x;
    for(auto &x:b)cin>>x;
    vector<ll>cnt(10);
    for(auto x:a)for(auto y:b){
        string s=to_string(x*y);
        for(auto c:s){
            cnt[c-'0']++;
        }
    }
    for(int i=0;i<10;i++){
        cout<<cnt[i]<<" \n"[i==9];
    }
    return true;
}

int main(){
    ll t=1;
//    in(t);
    while(solve());
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3244
final_rank: 4