Problem:
Problem E:
Cards
There are many blue cards and red cards on the table.
For each card, an integer number greater than 1 is printed on its face.
The same number may be printed on several cards.
A blue card and a red card can be paired when both of the numbers
printed on them have a common divisor greater than 1.
There may be more than one red card that can be paired with one blue card.
Also, there may be more than one blue card that can be paired with one red card.
When a blue card and a red card are chosen and paired,
these two cards are removed from the whole cards on the table.
Figure E-1: Four blue cards and three red cards
For example, in Figure E-1,
there are four blue cards and three red cards.
Numbers 2, 6, 6 and 15 are printed on the faces of the four blue cards, 
and 2, 3 and 35 are printed on those of the three red cards.
Here, you can make pairs of blue cards and red cards as follows.
First,
the blue card with number 2 on it and the red card with 2 are paired and removed.
Second,
one of the two blue cards with 6 and the red card with 3 are paired and removed.
Finally,
the blue card with 15 and the red card with 35 are paired and removed.
Thus the number of removed pairs is three.
Note that the total number of the pairs depends on
the way of choosing cards to be paired.
The blue card with 15 and the red card with 3 might be paired 
and removed at the beginning.
In this case, there are only one more pair that can be removed
and the total number of the removed pairs is two.
Your job is to find the largest number of pairs that can be removed from the given set of cards on the table.
Input
The input is a sequence of datasets.
The number of the datasets is less than or equal to 100.
Each dataset is formatted as follows.
m
n
b
1
...
b
k
...
b
m
r
1
...
r
k
...
r
n
The integers
m
and
n
are the number of blue cards
and that of red cards, respectively.
You may assume 1 ≤
m
≤ 500 and 1≤
n
≤ 500.
b
k
(1 ≤
k
≤
m
) and
r
k
(1 ≤
k
≤
n
) are
numbers printed on the blue cards and the red cards respectively,
that are integers greater than or equal to 2 and less than 
10000000 (=10
7
). 
The input integers are separated by a space or a newline.
Each of
b
m
and
r
n
is
followed by a newline.
There are no other characters in the dataset.
The end of the input is indicated by a line containing two zeros
separated by a space.
Output
For each dataset, output a line containing an integer that indicates
the maximum of the number of the pairs.
Sample Input
4 3
2 6 6 15
2 3 5
2 3
4 9
8 16 32
4 2
4 9 11 13
5 7
5 5
2 3 5 1001 1001
7 11 13 30 30
10 10
2 3 5 7 9 11 13 15 17 29
4 6 10 14 18 22 26 30 34 38
20 20
195 144 903 63 137 513 44 626 75 473
876 421 568 519 755 840 374 368 570 872
363 650 155 265 64 26 426 391 15 421
373 984 564 54 823 477 565 866 879 638
100 100
195 144 903 63 137 513 44 626 75 473
876 421 568 519 755 840 374 368 570 872
363 650 155 265 64 26 426 391 15 421
373 984 564 54 823 477 565 866 879 638
117 755 835 683 52 369 302 424 513 870
75 874 299 22
...(truncated)

Submitted solutions:

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

int dfs(vector<vector<tuple<int,int,int>>>& G,vector<bool>& checked,int cv,int cf,int goal){
    if(cv==goal) return cf;
    checked[cv]=true;

    int ca=0;
    for(auto& nv:G[cv]){
        if(checked[get<0>(nv)]||get<1>(nv)==0) continue;
        ca=dfs(G,checked,get<0>(nv),min(get<1>(nv),cf),goal);
        if(ca>0){
            get<1>(nv)-=ca;
            get<1>(G[get<0>(nv)][get<2>(nv)])+=ca;
            return ca;
        }
    }

    return 0;
}

int main(){
    while(true){
        int X,Y; cin>>X>>Y;
        if(X==0&&Y==0) return 0;
        vector<int> A(X),B(Y);
        for(int& a:A) cin>>a;
        for(int& b:B) cin>>b;
        vector<vector<tuple<int,int,int>>> G(X+Y+2); //v, c, zansa
        for(int x=0;x<X;x++){
            for(int y=0;y<Y;y++){
                if(gcd(A[x],B[y])==1) continue;
                G[x].push_back({y+X,1,G[y+X].size()});
                G[y+X].push_back({x,0,G[x].size()-1});
            }
        }
        for(int i=0;i<X;i++){
            G[X+Y].push_back({i,1,G[i].size()});
            G[i].push_back({X+Y,0,G[X+Y].size()-1});
        }
        for(int i=0;i<Y;i++){
            G[i+X].push_back({X+Y+1,1,G[X+Y+1].size()});
            G[X+Y+1].push_back({i+X,0,G[i+X].size()-1});
        }

        int ans=0;
        while(true){
            vector<bool> checked(X+Y+2,false);
            int ca=dfs(G,checked,X+Y,1e9,X+Y+1);
            ans+=ca;
            if(ca==0) break;
        }
        cout<<ans<<endl;
    }
}
accuracy: 1
time_ms: 1020
memory_kb: 7404
final_rank: 9

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


#define REP(i,n) for(int i=0;i<(int)n;++i)
#define FOR(i,c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();++i)
#define ALL(c) (c).begin(), (c).end()

typedef int Weight;
struct Edge {
  int src, dst;
  Weight weight;
  Edge(int src, int dst, Weight weight=1) :
    src(src), dst(dst), weight(weight) { }
};
bool operator < (const Edge &e, const Edge &f) {
  return e.weight != f.weight ? e.weight > f.weight : // !!INVERSE!!
    e.src != f.src ? e.src < f.src : e.dst < f.dst;
}
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;

typedef vector<Weight> Array;
typedef vector<Array> Matrix;

bool augment(const Graph& g, int u,
    vector<int>& matchTo, vector<bool>& visited) {
  if (u < 0) return true;
  FOR(e, g[u]) if (!visited[e->dst]) {
    visited[e->dst] = true;
    if (augment(g, matchTo[e->dst], matchTo, visited)) {
      matchTo[e->src] = e->dst;
      matchTo[e->dst] = e->src;
      return true;
    }
  }
  return false;
}
int bipartiteMatching(const Graph& g, int L) {
  const int n = g.size();
  vector<int> matchTo(n, -1);
  int match = 0;
  REP(u, L) {
    vector<bool> visited(n);
    if (augment(g, u, matchTo, visited)) ++match;
  }
  return match;
}

int main(){
	int n,m;
	while( cin >> n >> m and n){
		Graph g(n+m);
		int A[510],B[510];
		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++)
				if( __gcd(A[i],B[j]) != 1 )
					g[i].push_back(Edge(i,n+j)),
					g[n+j].push_back(Edge(n+j,i));

		cout << bipartiteMatching(g,n) << endl;
	}
}
accuracy: 1
time_ms: 740
memory_kb: 7608
final_rank: 4

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;
using pll = pair<ll, ll>;
const ll INF = LLONG_MAX / 4;
const ll mod1 = 1000000007;
const ll mod9 = 998244353;
const ll Hash = 10000000000000061;
#define all(a) (a).begin(),(a).end()
#define rep(i, n) for (ll i = 0; (i) < (n); ++(i))
#define reps(i, l, r) for(ll i = (l); (i) < (r); ++(i))
ll dx[8] = {1, 1, 0, -1, -1, -1, 0, 1};
ll dy[8] = {0, -1, -1, -1, 0, 1, 1, 1};
template <typename T>
bool chmax(T &a, const T& b) {
    if (a < b) { a = b; return true; }
    return false;
}
template <typename T>
bool chmin(T &a, const T& b) {
    if (a > b) { a = b; return true; }
    return false;
}

#define EPS (1e-10)
#define equals(a, b) (fabs((a) - (b)) < EPS)
class Point {
public:
    ld x;
    ld y;
    Point(ld _x = 0, ld _y = 0) : x(_x), y(_y) {}
    Point operator+(Point p) { return Point(x + p.x, y + p.y); }
    Point operator-(Point p) { return Point(x - p.x, y - p.y); }
    Point operator*(ld a) { return Point(a * x, a * y); }
    Point operator/(ld a) { return Point(x / a, y / a); }

    ld abs() { return sqrt(norm()); }
ld norm() { return x * x + y * y; }

    bool operator<(const Point& p) const {
        return !equals(x, p.x) ? x < p.x : y < p.y;
    }
    bool operator==(const Point& p) const {
        return fabs(x - p.x) < (1e-10) && fabs(y - p.y) < (1e-10);
    }
};
typedef Point Vector;
struct Segment {
    Point A;
    Point B;
};
typedef Segment Line;
class Circle {
public:
    Point C;
    ld r;
    Circle(Point C = Point(), ld r = 0.0) : C(C), r(r) {}
};
typedef vector<Point> Polygon;

struct Edge {
    ll from, to, cost;
    Edge (ll from, ll to, ll cost = 1ll) : from(from), to(to), cost(cost) {}
};
struct Graph {
    vector<vector<Edge>> G;
    Graph() = default;
    explicit Graph(ll N) : G(N) {}
    size_t size() const {
        return G.size();
    }
    void add(ll from, ll to, ll cost = 1ll, bool direct = 0) {
        G[from].emplace_back(from, to, cost);
        if (!direct) G[to].emplace_back(to, from, cost);
    }
    vector<Edge> &operator[](const int &k) {
        return G[k];
    }
};
using Edges = vector<Edge>;

template <typename flow_t>
struct Dinic {
    const flow_t INF;

    struct edge {
        int to;
        flow_t cap;
        int rev;
        bool isrev;
        int idx;
    };

    vector<vector<edge> > graph;
    vector<int> min_cost, iter;

    explicit Dinic(int V) : INF(numeric_limits<flow_t>::max()), graph(V) {}

    void add_edge(int from, int to, flow_t cap, int idx = -1) {
        graph[from].emplace_back(
            (edge){to, cap, (int)graph[to].size(), false, idx});
        graph[to].emplace_back(
            (edge){from, 0, (int)graph[from].size() - 1, true, idx});
    }

    bool build_augment_path(int s, int t) {
        min_cost.assign(graph.size(), -1);
        queue<int> que;
        min_cost[s] = 0;
        que.push(s);
        while (!que.empty() && min_cost[t] == -1) {
            int p = que.front();
            que.pop();
            for (auto &e : graph[p]) {
                if (e.cap > 0 && min_cost[e.to] == -1) {
                    min_cost[e.to] = min_cost[p] + 1;
                    que.push(e.to);
                }
            }
        }
        return min_cost[t] != -1;
    }
    flow_t find_min_dist_augment_path(int idx, const int t, flow_t flow) {
        if (idx == t) return flow;
        for (int &i = iter[idx]; i < (int)graph[idx].size(); i++) {
            edge &e = graph[idx][i];
            if (e.cap > 0 && min_cost[idx] < min_cost[e.to]) {
                flow_t d = find_min_dist_augment_path(e.to, t, min(flow, e.cap));
                if (d > 0) {
                    e.cap -= d;
                    graph[e.to][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }
    flow_t max_flow(int s, int t) {
        flow_t flow = 0;
        while (build_augment_path(s, t)) {
            iter.assign(graph.size(), 0);
            flow_t f;
            while ((f = find_min_dist_augment_path(s, t, INF)) > 0) flow += f;
        }
        return flow;
    }
    void output() {
        for (int i = 0; i < graph.size(); i++) {
            for (auto &e : graph[i]) {
                if (e.isrev) continue;
                auto &rev_e = graph[e.to][e.rev];
                cerr << i << "->" << e.to << " (flow: " << rev_e.cap << "/" << e.cap + rev_e.cap << ")" << endl;
            }
        }
    }
    vector<bool> min_cut(int s) {
        vector<bool> used(graph.size());
        queue<int> que;
        que.emplace(s);
        used[s] = true;
        while (not que.empty()) {
            int p = que.front();
            que.pop();
            for (auto &e : graph[p]) {
                if (e.cap > 0 and not used[e.to]) {
                    used[e.to] = true;
                    que.emplace(e.to);
                }
            }
        }
        return used;
    }
};

bool solve(){
    ll N, M; cin >> N >> M;
    if(N == 0) return 0;
    vector<ll> a(N), b(M);
    rep(i, N) cin >> a[i];
    rep(i, M) cin >> b[i];
    Dinic<ll> mf(N+M+2);
    rep(i, N){
        rep(k, M){
            if(gcd(a[i], b[k]) > 1) mf.add_edge(i, k+N, 1);
        }
    }
    rep(i, N) mf.add_edge(N+M, i, 1);
    rep(i, M) mf.add_edge(i+N, M+N+1, 1);
    cout << mf.max_flow(N+M, N+M+1) << endl;
    return 1;
}

int main(){
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
    cout << fixed << setprecision(20);
    
    ll t = 1;
    while(t){
        t = solve();
    }
}
accuracy: 1
time_ms: 910
memory_kb: 14144
final_rank: 17

code_snippet: // #ifndef ONLINE_JUDGE
// #define _GLIBCXX_DEBUG//[]で配列外参照をするとエラーにしてくれる。上下のやつがないとTLEになるので注意 ABC311Eのサンプル4みたいなデバック中のTLEは防げないので注意
// #endif

#include <bits/stdc++.h>
using namespace std;
// #include <atcoder/all>
// using namespace atcoder;


template<typename T> using vc = vector<T>;//prioriy_queueに必要なのでここにこれ書いてます
template<typename T> using vv = vc<vc<T>>;

//-------------1.型系---------------
using ll = long long;
ll INF = 2e18;

// #include <boost/multiprecision/cpp_int.hpp>//インストール的なのをしてないとできないので注意
// namespace multip = boost::multiprecision;
// //using lll = multip::cpp_int;//無制限を使いたいときはこっちを使う
// using lll = multip::int128_t;

using ld = long double;
using bl = bool;
// using mint = modint998244353;
//using mint = modint1000000007;
//using mint = modint;//使うときはコメントアウトを外す
//mint::set_mod(m);//使うときはコメントアウトを外す

template<class T> using pq = priority_queue<T, vc<T>>;//大きい順
template<class T> using pq_g = priority_queue<T, vc<T>, greater<T>>;//小さい順
//-----------------------------------



//-------------2.配列系--------------
using vl = vc<ll>; using vvl = vv<ll>; using vvvl = vv<vl>; using vvvvl = vv<vvl>;
using vs = vc<string>; using vvs = vv<string>;
using vb = vc<bl>; using vvb = vv<bl>; using vvvb = vv<vb>;
using vld = vc<ld>; using vvld = vv<ld>; using vvvld = vv<vld>;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
// using vmint = vc<mint>; using vvmint = vv<mint>; using vvvmint = vv<vmint>;

//配列外参照対策のやつは一番上の行にあります

#define rep(i,n) for(ll i = 0; i < (n); ++i)//↓でrepを使うので書いてます
template<class T>istream& operator>>(istream& i, vc<T>& v) { rep(j, size(v))i >> v[j]; return i; }

using ar2 = array<ll, 2>;

//----------------------------------



//--------3.コード短縮化とか---------
#define rep(i,n) for(ll i = 0; i < (n); ++i)
#define rrep(i,n) for(ll i = 1; i <= (n); ++i)
#define drep(i,n) for(ll i = (n)-1; i >= 0; --i)
#define nfor(i,s,n) for(ll i=s;i<n;i++)//i=s,s+1...n-1 ノーマルfor
#define dfor(i,s,n) for(ll i = (s)-1; i>=n;i--)//s-1スタートでnまで落ちる

#define nall(a) a.begin(),a.end()
#define rall(a) a.rbegin(),a.rend()

#define chmax(x,y) x = max(x,y)
#define chmin(x,y) x = min(x,y)

#define pb push_back
#define eb emplace_back
#define em emplace
#define pob pop_back


#define YES cout<<"Yes"<<endl
#define NO cout<<"No"<<endl
#define YN {cout<<"Yes"<<endl;}else{cout<<"No"<<endl;}// if(a==b)YN;
#define dame cout<<-1<<endl


#define vc_unique(v) v.erase( unique(v.begin(), v.end()), v.end() );
#define vc_rotate(v) rotate(v.begin(),v.begin()+1,v.end());

#define pop_cnt(s) ll(popcount(uint64_t(s)))

#define next_p(v) next_permutation(v.begin(),v.end())

//if (regex_match(s, regex("")))YN;//文字列sの判定を行う。コメントアウトを外して「""」の中に判定する内容を入れる

//-------------------------------




//---------4.グリッド系----------
vl di = { 0,1,0,-1 };//vl di={0,1,1,1,0,-1,-1,-1};
vl dj = { 1,0,-1,0 };//vl dj={1,1,0,-1,-1,-1,0,1};

bool out_grid(ll i, ll j, ll h, ll w) {//trueならcontinue
    return (!(0 <= i && i < h && 0 <= j && j < w));
}

#define vvl_kaiten_r(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[j][n-i-1]=v[i][j];swap(nx,v);}//時計回りに90°回転
#define vvl_kaiten_l(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[n-j-1][i]=v[i][j];swap(nx,v);}//反時計周りに90°回転

#define vs_kaiten_r(v) {ll n = size(v);vs nx(n,string(n,'.'));rep(i,n)rep(j,n)nx[j][n-i-1]=v[i][j];swap(nx,v);}//文字列版 時計回りに90°回転
#define vs_kaiten_l(v) {ll n = size(v);vs nx(n,string(n,'.'));rep(i,n)rep(j,n)nx[n-j-1][i]=v[i][j];swap(nx,v);}//文字列版　反時計周りに90°回転


#define vvl_tenti(v) {ll n = size(v);vvl nx(n,vl(n));rep(i,n)rep(j,n)nx[j][i]=v[i][j];swap(nx,v);}
#define vs_tenti(v) {ll n = size(v); vs nx(n, string(n,'.')); rep(i, n)rep(j, n)nx[j][i] = v[i][j]; swap(nx, v);}

//--------------------------------




//-----------5.数学系--------------
#define yu_qurid(x,y) ((x)*(x)+(y)*(y))//ユークリッド距離 sqrtはしてないなので注意
#define mannhattan(x1,x2,y1,y2) (abs(x1-x2)+abs(y1-y2)) // マンハッタン距離 = |x1-x2|+|y1-y2|

template<class T>T tousa_sum1(T l, T d, T r) {//初項,公差,末項 で総和を求める
    T wide = (r - l) / d + 1;
    return (l + r) * wide / 2;
}
template<class T>T tousa_sum2(T a, T d, T n) {//初項,交差,項数 で総和を求める
    return (a * 2 + d * (n - 1)) * n / 2;
}
ll kousa_kousuu(ll l, ll r, ll d) {//初項,末項,交差 で等差数列の項数を求める
    return (r - l) / d + 1;
}
// mint touhi_sum(mint a, mint r, ll n) {//初項,公比,項数で等比数列の総和を求める
//     if (r == 1) {
//         return a * n;
//     }
//     mint bunsi = a * (r.pow(n) - mint(1));
//     mint bunbo = r - 1;
//     return bunsi / bunbo;
// }

ll nc2(ll x) { return x * (x - 1) / 2; }
ll nc3(ll x) { return x * (x - 1) * (x - 2) / 6; }

//----------------------------------------------




//-----------6.デバックや出力系------------------
void print(ld x) { printf("%.20Lf\n", x); }

void mukou_debug(vvl to, bool yukou) {//GRAPH × GRAPH用の無向グラフを出力する
    ll n = size(to); ll cnt = 0;//辺の本数
    vc<pair<ll, ll>>v; rep(i, n)for (ll t : to[i])  if (i < t || yukou)cnt++, v.eb(i + 1, t + 1);//有向グラフなら全部OK、違うなら無向なのでf<tのみ見る、using Pのやつを別のにしたいときのためにPを使わずにpair<ll,ll>にしてる
    cout << n << ' ' << cnt << endl; for (auto [f, t] : v)cout << f << ' ' << t << endl;
}

#define vc_cout(v){ll n = size(v);rep(i,n)cout<<v[i]<<endl;}//一次元配列を出力する
#define vv_cout(v){ll n = size(v);rep(i,n){rep(j,size(v[i])){cout<<v[i][j]<<' ';}cout<<endl;}}//二次元配列を出力する

// edge class (for network-flow)
template<class FLOWTYPE> struct FlowEdge {
    // core members
    int rev, from, to;
    FLOWTYPE cap, icap, flow;
    
    // constructor
    FlowEdge(int r, int f, int t, FLOWTYPE c)
    : rev(r), from(f), to(t), cap(c), icap(c), flow(0) {}
    void reset() { cap = icap, flow = 0; }
    
    // debug
    friend ostream& operator << (ostream& s, const FlowEdge& E) {
        return s << E.from << "->" << E.to << '(' << E.flow << '/' << E.icap << ')';
    }
};

// graph class (for network-flow)
template<class FLOWTYPE> struct FlowGraph {
    // core members
    vector<vector<FlowEdge<FLOWTYPE>>> list;
    vector<pair<int,int>> pos;  // pos[i] := {vertex, order of list[vertex]} of i-th edge
    
    // constructor
    FlowGraph(int n = 0) : list(n) { }
    void init(int n = 0) {
        list.assign(n, FlowEdge<FLOWTYPE>());
        pos.clear();
    }
    
    // getter
    vector<FlowEdge<FLOWTYPE>> &operator [] (int i) {
        return list[i];
    }
    const vector<FlowEdge<FLOWTYPE>> &operator [] (int i) const {
        return list[i];
    }
    size_t size() const {
        return list.size();
    }
    FlowEdge<FLOWTYPE> &get_rev_edge(const FlowEdge<FLOWTYPE> &e) {
        if (e.from != e.to) return list[e.to][e.rev];
        else return list[e.to][e.rev + 1];
    }
    FlowEdge<FLOWTYPE> &get_edge(int i) {
        return list[pos[i].first][pos[i].second];
    }
    const FlowEdge<FLOWTYPE> &get_edge(int i) const {
        return list[pos[i].first][pos[i].second];
    }
    vector<FlowEdge<FLOWTYPE>> get_edges() const {
        vector<FlowEdge<FLOWTYPE>> edges;
        for (int i = 0; i < (int)pos.size(); ++i) {
            edges.push_back(get_edge(i));
        }
        return edges;
    }
    
    // change edges
    void reset() {
        for (int i = 0; i < (int)list.size(); ++i) {
            for (FlowEdge<FLOWTYPE> &e : list[i]) e.reset();
        }
    }
    void change_edge(FlowEdge<FLOWTYPE> &e, FLOWTYPE new_cap, FLOWTYPE new_flow) {
        FlowEdge<FLOWTYPE> &re = get_rev_edge(e);
        e.cap = new_cap - new_flow, e.icap = new_cap, e.flow = new_flow;
        re.cap = new_flow;
    }
    
    // add_edge
    void add_edge(int from, int to, FLOWTYPE cap) {
        pos.emplace_back(from, (int)list[from].size());
        list[from].push_back(FlowEdge<FLOWTYPE>((int)list[to].size(), from, to, cap));
        list[to].push_back(FlowEdge<FLOWTYPE>((int)list[from].size() - 1, to, from, 0));
    }

    // debug
    friend ostream& operator << (ostream& s, const FlowGraph &G) {
        const auto &edges = G.get_edges();
        for (const auto &e : edges) s << e << endl;
        return s;
    }
};

// Dinic
template<class FLOWTYPE> FLOWTYPE Dinic
 (FlowGraph<FLOWTYPE> &G, int s, int t, FLOWTYPE limit_flow)
{
    FLOWTYPE current_flow = 0;
    vector<int> level((int)G.size(), -1), iter((int)G.size(), 0);
    
    // Dinic BFS
    auto bfs = [&]() -> void {
        level.assign((int)G.size(), -1);
        level[s] = 0;
        queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int v = que.front();
            que.pop();
            for (const FlowEdge<FLOWTYPE> &e : G[v]) {
                if (level[e.to] < 0 && e.cap > 0) {
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        }
    };
    
    // Dinic DFS
    auto dfs = [&](auto self, int v, FLOWTYPE up_flow) {
        if (v == t) return up_flow;
        FLOWTYPE res_flow = 0;
        for (int &i = iter[v]; i < (int)G[v].size(); ++i) {
            FlowEdge<FLOWTYPE> &e = G[v][i], &re = G.get_rev_edge(e);
            if (level[v] >= level[e.to] || e.cap == 0) continue;
            FLOWTYPE flow = self(self, e.to, min(up_flow - res_flow, e.cap));
            if (flow <= 0) continue;
            res_flow += flow;
            e.cap -= flow, e.flow += flow;
            re.cap += flow, re.flow -= flow;
            if (res_flow == up_flow) break;
        }
        return res_flow;
    };
    
    // flow
    while (current_flow < limit_flow) {
        bfs();
        if (level[t] < 0) break;
        iter.assign((int)iter.size(), 0);
        while (current_flow < limit_flow) {
            FLOWTYPE flow = dfs(dfs, s, limit_flow - current_flow);
            if (!flow) break;
            current_flow += flow;
        }
    }
    return current_flow;
};

template<class FLOWTYPE> FLOWTYPE Dinic(FlowGraph<FLOWTYPE> &G, int s, int t) {
    return Dinic(G, s, t, numeric_limits<FLOWTYPE>::max());
}


int main(){
    vl ans;

    while(1){
        ll m, n;
        cin >> m >> n;

        if(m == 0 && n == 0) break;

        vl b(m), r(n);
        rep(i, m) cin >> b[i];
        rep(j, n) cin >> r[j];
    
        FlowGraph<ll> G(m+n+2);
        rep(i, m){
            G.add_edge(m+n, i, 1);
        }
        rep(j, n){
            G.add_edge(m+j, m+n+1, 1);
        }
    
        rep(i, m) rep(j, n){
            if(gcd(b[i], r[j]) > 1){
                G.add_edge(i, m+j, 1);
            }
        }
    
        ans.pb(Dinic(G, m+n, m+n+1));
    }

    for(ll a : ans) cout << a << endl;

    return 0;
}
accuracy: 1
time_ms: 930
memory_kb: 17256
final_rank: 19

code_snippet: #define _USE_MATH_DEFINES
#include <bits/stdc++.h>
using ll = long long;
using ull = unsigned long long;
namespace internal {

    template <class T> struct simple_queue {
        std::vector<T> payload;
        int pos = 0;
        void reserve(int n) { payload.reserve(n); }
        int size() const { return int(payload.size()) - pos; }
        bool empty() const { return pos == int(payload.size()); }
        void push(const T& t) { payload.push_back(t); }
        T& front() { return payload[pos]; }
        void clear() {
            payload.clear();
            pos = 0;
        }
        void pop() { pos++; }
    };

}  // namespace internal
template <class Cap> struct mf_graph {
public:
    mf_graph() : _n(0) {}
    explicit mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({ from, int(g[from].size()) });
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{ to, to_id, cap });
        g[to].push_back(_edge{ from, from_id, 0 });
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{ pos[i].first, _e.to, _e.cap + _re.cap, _re.cap };
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d =
                    self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) return res;
            }
            level[v] = _n;
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            Cap f = dfs(dfs, t, flow_limit - flow);
            if (!f) break;
            flow += f;
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

int main()
{
    ll m, n;
    while (std::cin >> m >> n)
    {
        if (m == 0)
            break;
        std::vector<ll>b(m);
        std::vector<ll>r(n);
        for (ll i = 0; i < m; i++)
        {
            std::cin >> b[i];
        }
        for (ll j = 0; j < n; j++)
        {
            std::cin >> r[j];
        }
        mf_graph<ll> graph(n + m + 2);
        for (ll i = 0; i < m; i++)
        {
            graph.add_edge(m + n, i, 1);
        }
        for (ll i = 0; i < n; i++)
        {
            graph.add_edge(m + i, n+m+1, 1);
        }
        for (ll i = 0; i < m; i++)
        {
            for (ll j = 0; j < n; j++)
            {
				if (std::gcd(b[i], r[j]) > 1)
					graph.add_edge(i, j + m, 1);
            }
        }
        std::cout << graph.flow(n + m, n + m + 1)<<"\n";
    }
}
accuracy: 1
time_ms: 890
memory_kb: 9968
final_rank: 11

code_snippet: #include <algorithm>

#include <vector>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap> struct mf_graph {
  public:
    mf_graph() : _n(0) {}
    mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap});
        g[to].push_back(_edge{from, from_id, 0});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d =
                    self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) break;
            }
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            while (flow < flow_limit) {
                Cap f = dfs(dfs, t, flow_limit - flow);
                if (!f) break;
                flow += f;
            }
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

  private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

constexpr ll INF = 2e18;

void solve(int m, int n, vector<ll> b, vector<ll> r) {
    atcoder::mf_graph<int> graph(m + n + 2);
    for (int i = 0; i < m; i++) {
        graph.add_edge(0, i + 1, 1);
    }
    for (int j = 0; j < n; j++) {
        graph.add_edge(j + m + 1, m + n + 1, 1);
    }
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (gcd(b[i], r[j]) != 1) {
                graph.add_edge(i + 1, j + m + 1, 1);
            }
        }
    }
    cout << graph.flow(0, n + m + 1) << endl;
}

int main() {
    while (true) {
        int m, n;
        cin >> m >> n;
        if (m == 0 && n == 0) {
            break;
        }

        vector<ll> b(m);
        vector<ll> r(n);
        for (int i = 0; i < m; i++) {
            cin >> b[i];
        }
        for (int i = 0; i < n; i++) {
            cin >> r[i];
        }

        solve(m, n, b, r);
    }
}
accuracy: 1
time_ms: 430
memory_kb: 8712
final_rank: 7

code_snippet: #include <atcoder/maxflow>
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

constexpr ll INF = 2e18;

// 14:58~
void solve(int m, int n, vector<ll> b, vector<ll> r) {
    atcoder::mf_graph<int> graph(m + n + 2);
    for (int i = 0; i < m; i++) {
        graph.add_edge(0, i + 1, 1);
    }
    for (int j = 0; j < n; j++) {
        graph.add_edge(j + m + 1, m + n + 1, 1);
    }
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (gcd(b[i], r[j]) != 1) {
                graph.add_edge(i + 1, j + m + 1, 1);
            }
        }
    }
    cout << graph.flow(0, n + m + 1) << endl;
}

int main() {
    while (true) {
        int m, n;
        cin >> m >> n;
        if (m == 0 && n == 0) {
            break;
        }

        vector<ll> b(m);
        vector<ll> r(n);
        for (int i = 0; i < m; i++) {
            cin >> b[i];
        }
        for (int i = 0; i < n; i++) {
            cin >> r[i];
        }

        solve(m, n, b, r);
    }
}
accuracy: 1
time_ms: 430
memory_kb: 8708
final_rank: 6

code_snippet: #include <atcoder/maxflow>
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

constexpr ll INF = 2e18;

void solve(int m, int n, vector<ll> b, vector<ll> r) {
    atcoder::mf_graph<int> graph(m + n + 2);
    for (int i = 0; i < m; i++) {
        graph.add_edge(0, i + 1, 1);
    }
    for (int j = 0; j < n; j++) {
        graph.add_edge(j + m + 1, m + n + 1, 1);
    }
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (gcd(b[i], r[j]) != 1) {
                graph.add_edge(i + 1, j + m + 1, 1);
            }
        }
    }
    cout << graph.flow(0, n + m + 1) << endl;
}

int main() {
    while (true) {
        int m, n;
        cin >> m >> n;
        if (m == 0 && n == 0) {
            break;
        }

        vector<ll> b(m);
        vector<ll> r(n);
        for (int i = 0; i < m; i++) {
            cin >> b[i];
        }
        for (int i = 0; i < n; i++) {
            cin >> r[i];
        }

        solve(m, n, b, r);
    }
}
accuracy: 1
time_ms: 420
memory_kb: 8708
final_rank: 3

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

#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>


#include <vector>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder


namespace atcoder {

template <class Cap> struct mf_graph {
  public:
    mf_graph() : _n(0) {}
    explicit mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap});
        g[to].push_back(_edge{from, from_id, 0});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d =
                    self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) return res;
            }
            level[v] = _n;
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            Cap f = dfs(dfs, t, flow_limit - flow);
            if (!f) break;
            flow += f;
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

  private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

using namespace std;
using namespace atcoder;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
#define rep(i, n) for (ll i = 0; i < (ll)(n); i++)
#define rrep(i,start,end) for (ll i = start;i >= (ll)(end);i--)
#define repn(i,end) for(ll i = 0; i <= (ll)(end); i++)
#define reps(i,start,end) for(ll i = start; i < (ll)(end); i++)
#define repsn(i,start,end) for(ll i = start; i <= (ll)(end); i++)
#define each(p,a) for(auto &p:a)
typedef vector<ll> vll;
typedef vector<pair<ll ,ll>> vpll;
typedef vector<vector<ll>> vvll;
typedef set<ll> sll;
typedef map<ll , ll> mpll;
typedef pair<ll ,ll> pll;
typedef tuple<ll , ll , ll> tpl3;
#define LL(...) ll __VA_ARGS__; input(__VA_ARGS__)
#define LD(...) ld __VA_ARGS__; input(__VA_ARGS__)
#define Str(...) string __VA_ARGS__; input(__VA_ARGS__)
#define Ch(...) char __VA_ARGS__; input(__VA_ARGS__)
#define all(a)  (a).begin(),(a).end()
#define UNIQUE(v) v.erase( unique(v.begin(), v.end()), v.end() );
#define sz(x) (ll)x.size()
// << std::fixed << std::setprecision(10)
const ll INF = 1LL << 60;
const ld EPS = 1e-9;
 
ll lceil(ll a,ll b){if(a%b==0){return a/b;}if(a>=0){return (a/b)+1;}else{return -((-a)/b);}}
ll lfloor(ll a,ll b){if(a%b==0){return a/b;}if(a>=0){return (a/b);}else{return -((-a)/b)-1;}}
inline ll positive_mod(ll a,ll m){return (a % m + m)%m;}
inline ll popcnt(ull a){ return __builtin_popcountll(a);}
//0indexed
inline ll topbit(ll a){assert(a != 0);return 63 - __builtin_clzll(a);}
inline ll smlbit(ll a){assert(a != 0);return __builtin_ctzll(a);}
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;}
template<typename T> std::istream &operator>>(std::istream&is,std::vector<T>&v){for(T &in:v){is>>in;}return is;}
template<typename T> std::ostream &operator<<(std::ostream&os,const std::vector<T>&v){for(auto it=std::begin(v);it!=std::end(v);){os<<*it<<((++it)!=std::end(v)?" ":"");}return os;}
template<typename T1, typename T2>std::ostream &operator<< (std::ostream &os, std::pair<T1,T2> p){os << "{" << p.first << "," << p.second << "}";return os;}
template<class... T>void input(T&... a){(cin >> ... >> a);}
void print(){cout << endl;}
template<class T, class... Ts>void print(const T& a, const Ts&... b){cout << a;((cout << ' ' << b), ...);cout << endl;}
template<class T> void pspace(const T& a){ cout << a << ' ';}
void perr(){cerr << endl;}
template<class T, class... Ts>void perr(const T& a, const Ts&... b){cerr << a;((cerr << ' ' << b), ...);cerr << endl;}
void yes(bool i = true){ return print(i?"yes":"no"); }
void Yes(bool i = true){ return print(i?"Yes":"No"); }
void YES(bool i = true){ return print(i?"YES":"NO"); }
template <class T> vector<T> &operator++(vector<T> &v) {for(auto &e : v) e++;return v;}
template <class T> vector<T> operator++(vector<T> &v, signed) {auto res = v;for(auto &e : v) e++;return res;}
template <class T> vector<T> &operator--(vector<T> &v) {for(auto &e : v) e--;return v;}
template <class T> vector<T> operator--(vector<T> &v, signed) {auto res = v;for(auto &e : v) e--;return res;}
//grid探索用
vector<ll> _ta = {0,0,1,-1,1,1,-1,-1};
vector<ll> _yo = {1,-1,0,0,1,-1,1,-1};
bool isin(ll now_i,ll now_j,ll h,ll w){return (0<=now_i && now_i < h && 0 <= now_j && now_j < w);}
  
ll lpow(ll x,ll n){ll ans = 1;while(n >0){if(n & 1)ans *= x;x *= x;n >>= 1;}return ans;}
ll Modlpow(ll x,ll n,ll m){ll ans = 1;ll a = x%m;while(n >0){if(n & 1){ans *= a;ans%= m;}a *= a;a %= m;n >>= 1;}return ans;} 
const ll MOD9 = 998244353LL;
const ll MOD10 = 1000000007LL;

// for AOJ or ICPC or etc..
template<class Tp>
bool zero (const Tp &x) {
    return x == 0;
}

template<class Tp, class... Args>
bool zero (const Tp &x, const Args& ...args) {
    return zero(x) and zero(args...);
}


//ref https://qiita.com/drken/items/3beb679e54266f20ab63#5-%E6%B4%BB%E7%94%A8%E4%BE%8B-2-%E3%83%A1%E3%83%93%E3%82%A6%E3%82%B9%E9%96%A2%E6%95%B0%E5%80%A4%E3%81%AE%E5%88%97%E6%8C%99
struct Eratosthenes{
  ll n;
  vector<bool> isprime;//素数ならtrue
  vector<ll> minfact;//xを割り切る一番小さい数
  vector<ll> mobius;// メビウス関数値
  /*
  メビウス関数u(x)とは
  u(1) = 1;
  xがある素数pで2回以上割り切れるとき、u(x) = 0;
  x = p1 * p2 * p3 * p4...pKと素因数分解できるときu(x) = (-1)^k 
  u(1)=1,u(2)=-1,u(3)=-1,u(4)=0,u(5)=-1,u(6)=1,u(7)=-1,u(8)=0,u(9)=0,u(10)=1
  */
  Eratosthenes(ll N){//O(NloglogN)
    n = N;
    isprime.resize(n+1,true);
    minfact.resize(n+1,-1);
    mobius.resize(n+1,1);
    isprime[0] = false;
    isprime[1] = false;
    minfact[1] = 1;
    for(ll p = 2;p <= n;p++){
      if(!isprime[p])continue;
      //pの情報を更新
      minfact[p] = p;
      mobius[p] = -1;
      for(ll q = p*2;q <= n;q+=p){
        isprime[q] = false;
        if(minfact[q] == -1)minfact[q] = p;
        if((q/p)%p==0)mobius[q]=0;//qはpで2回以上割れる
        else mobius[q] = -mobius[q];//反転
      }
    }
  }

  bool is_prime(ll x){
    assert(x <= n);
    return isprime[x];
  }

  //素因数分解(log(x))
  vector<pair<ll,ll>> soinsu_bunkai(ll x){
    vector<pair<ll,ll>> ret;
    while(x > 1){
      ll p = minfact[x];
      ll times = 0;//割り切れる回数
      while(minfact[x] == p){
        x/= p;
        times++;
      }
      ret.push_back({p,times});
    }
    return ret;
  }

  //約数列挙 O(約数の個数)　n <= 10^9 のとき 高々1344
  vector<ll> yakusu_rekkyo(ll x){
    vector<ll> ret;
    ret.push_back(1);
    vector<pair<ll,ll>> pf = soinsu_bunkai(x);
    for(auto &p:pf){
      ll siz = ret.size();
      for(ll i = 0;i < siz;i++){
        ll v = 1;
        for(ll j = 0;j < p.second;j++){
          v *= p.first;
          ret.push_back(ret[i]*v);
        }
      }
    }
    return ret;
  }
};

// 高速ゼータ変換
// 入力 f が in-place に更新されて、F になる
template<class T> void fast_zeta(vector<T> &f){
  ll n = f.size();
  Eratosthenes era(n);
  vector<bool> isprime = era.isprime;
  // 各素数 p 軸に対して
  // 大きい座標 (k * p) から小さい座標 (k) へと足し込む
  for(ll p = 2;p < n;p++){
    if(!isprime[p])continue;
    for(ll k = (n-1)/p;k >= 1;k--){
      f[k] += f[k * p];
    }
  }
} 
// 高速メビウス変換
// 入力 F が in-place に更新されて、f になる
template<class T> void fast_mobius(vector<T> &F){
  ll n = F.size();
  Eratosthenes era(n);
  vector<bool> isprime = era.isprime;
  // 各素数 p 軸に対して
  // 小さい座標 (k) から大きい座標 (k * p) を引いていく
  for(ll p = 2;p < n;p++){
    if(!isprime[p])continue;
    for(ll k = 1;k * p < n;k++){
      F[k] -= F[k * p];
    }
  }
}

//約数列挙
vector<ll> divisor(ll n){
  vector<ll> ret;
  for(ll i = 1;i * i <= n;i++){
    if(n % i == 0){
      ret.push_back(i);
      if(i * i != n)ret.push_back(n/i);
    }
  }
  //昇順
  sort(all(ret));
  return ret;
}

void solve(ll m,ll n){
  vll b(m),r(n);cin >> b >> r;

  vector<set<ll>> rs(n);
  rep(i,n){
    vll ret = divisor(r[i]);
    each(p,ret)if(p!= 1)rs[i].insert(p);
  }
  mf_graph<ll> mf(n + m + 2);
  vvll edges(m,vll(n));
  rep(i,m){
    vll ret  =divisor(b[i]);
    each(p,ret)if(p != 1)rep(j,n)if(rs[j].contains(p) && edges[i][j] == 0){
      edges[i][j] = 1;
      mf.add_edge(i,m+j,1);
    }
  }
  rep(i,m){
    mf.add_edge(n+m,i,1);
  }
  rep(i,n){
    mf.add_edge(m+i,n+m+1,1);
  }
  ll ret = mf.flow(n+m,n+m+1);
  cout << ret << endl;
}
 
int main(){
  ios::sync_with_stdio(false);cin.tie(nullptr);
  // Eratosthenes era(lpow(10,7));
  while(true){
    LL(m,n);
    if(zero(m,n))break;
    solve(m,n);
  }
}
accuracy: 1
time_ms: 1110
memory_kb: 12616
final_rank: 16

code_snippet: #include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <vector>

using namespace std;
// https://cpprefjp.github.io/reference/numeric/gcd.html
template <class M, class N> constexpr std::common_type_t<M, N> gcd(M m, N n) {
    if (m == 0 && n == 0) {
        return 0;
    }

    auto mm = abs(m);
    auto nn = abs(n);
    while (mm != 0 && nn != 0) {
        if (mm > nn) {
            mm %= nn;
        } else {
            nn %= mm;
        }
    }
    return mm < nn ? nn : mm;
}

// グラフを表す構造体
struct Graph {
    // 辺を表す構造体
    // rev: 逆元 (to, from) が G[to] の中で何番目の要素か
    // cap: 辺 (from, to) の容量
    struct
        Edge { // グラフ構造体のスコープ内で使える構造体，mainとかでは使えない
        int rev, from, to, cap;
        Edge(int r, int f, int t, int c)
            : rev(r), from(f), to(t), cap(c) {} // reverse, from, to, capacity
    };
    // 隣接リスト
    vector<vector<Edge>> list; // Edge の実態が list

    // N: 頂点数 <- コンストラクタ(構造体の初期化のときにのみ呼ばれる，__init__
    // のこと) 構造体と名前が一緒
    Graph(int N = 0) : list(N) {}
    ~Graph() {} // ディストラクタ

    // グラフの頂点数取得，インスタンスメソッド
    size_t size() { return list.size(); }

    // Graph インスタンスをGとして
    // G.list[v] を　G[v] とかけるようにしておく
    vector<Edge>& operator[](int i) { return list[i]; }

    // 辺 e = (u, v) の逆辺 (v, u) を取得
    Edge& redge(const Edge& e) { return list[e.to][e.rev]; }

    // 辺 e = (u, v) に流量 f のフローを流す
    // 辺 e = (u, v) の流量が f だけ減少する
    // このとき，逆辺 e' = (v, u) の流量を増やす
    void run_flow(Edge& e, int f) {
        e.cap -= f;
        redge(e).cap += f;
    }

    // 頂点 from から頂点 to へ容量 cap の辺を張る
    // このとき to から from への容量 0 の辺を張っておく
    void addedge(int from, int to, int cap) {
        int fromrev = (int)list[from].size();
        int torev = (int)list[to].size(); // 逆辺を追加するインデックスに相当
        list[from].push_back(Edge(torev, from, to, cap));
        list[to].push_back(Edge(fromrev, to, from, 0)); // ぎゃくへｎ
    }
};

struct FordFulkerson {
    static const int INF = 1 << 30; // 無限大を表す値を適切に
    vector<int> seen;

    FordFulkerson() {}

    // 残余グラフ上で s-t パスを見つける(深さ優先探索)
    // 戻り値は s-t パス上の容量の最小値(見つからなかったら 0)
    // f: s から v へ到達した過程の各辺の容量の最小値
    int fodfs(Graph& G, int v, int t, int f) {
        // 終端 t に到達したらリターン
        if (v == t) return f;

        // 深さ優先探索
        seen[v] = true;
        for (auto& e : G[v]) {
            if (seen[e.to]) continue;

            // 容量0の辺は実際には存在しない
            if (e.cap == 0) continue;

            // s-t パスを探す
            // 見つかったら flow はパス上の最小容量
            // 見つからなかったら f = 0
            int flow = fodfs(G, e.to, t, min(f, e.cap));

            // s-t パスが見つからなかったら次辺を試す
            if (flow == 0) continue;

            // 辺 e に容量 flow のフローを流す
            G.run_flow(e, flow);

            // s-t パスを見つけたらパス上最小容量を返す
            return flow;
        }

        return 0;
    }

    // グラフ G の s-t 間の最大流量を求める
    // ただしリターン時に G は残余グラフになる

    int solve(Graph& G, int s, int t) {
        int res = 0;
        // 残余グラフに s-t パスがなくなるまで反復
        while (true) {
            seen.assign((int)G.size(), 0);
            int flow = fodfs(G, s, t, INF);

            // s-t パスが見つからなかったら終了
            if (flow == 0) return res;

            // 答えを加算
            res += flow;
        }
    }
};

int main() {
    while (1) {
        int m, n;
        cin >> m >> n;
        if (m == 0 && n == 0) break;
        vector<int> b(m), r(n);
        for (int i = 0; i < m; i++) {
            cin >> b[i];
        }
        for (int i = 0; i < n; i++) {
            cin >> r[i];
        }

        // 約数を持つカード同士をつないでグラフを作成する
        // 頂点のindex：
        //      始点s: 0
        //      青のカード:  1, ... ,m
        //      赤のカード:  m + 1, ... , m + n
        //      終点: m + n + 1
        // 全頂点は 0 ... m + n + 1 なので m + n + 1 + 1個
        Graph Graph(m + n + 1 + 1);
        // cout << "----公約数を持つカード----" << endl;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (gcd(b[i], r[j]) > 1) { // 互いに素ならgcd=1
                    Graph.addedge(1 + i, 1 + m + j, 1);

                    // cout << 1 + i << "," << 1 + m + j << endl;
                }
            }
        }
        // cout << "--------" << endl;
        // s=0を青のカードにつなぐ
        for (int i = 0; i < m; i++) {
            Graph.addedge(0, 1 + i, 1);
        }
        // 赤のカードを終点m+n+1につなぐ
        for (int j = 0; j < n; j++) {
            Graph.addedge(1 + m + j, m + n + 1, 1);
        }
        FordFulkerson ff;
        int s = 0, t = m + n + 1;
        cout << ff.solve(Graph, s, t) << endl;
    }
}

/*
入力：
2 3
4 9
8 16 32
出力：
2 → 1

入力：
4 2
4 9 11 13
5 7
出力：
2 → 0
*/
accuracy: 1
time_ms: 990
memory_kb: 9012
final_rank: 10

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


#include <algorithm>

#include <vector>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

template <class Cap> struct mf_graph {
  public:
    mf_graph() : _n(0) {}
    mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap});
        g[to].push_back(_edge{from, from_id, 0});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d =
                    self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) break;
            }
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            while (flow < flow_limit) {
                Cap f = dfs(dfs, t, flow_limit - flow);
                if (!f) break;
                flow += f;
            }
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

  private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder


bool solve(){
    int m, n; cin >> m >> n;
    if(m == 0) return false;

    vector<int> b(m), r(n);
    for(int i = 0; i < m; ++i) cin >> b[i];
    for(int i = 0; i < n; ++i) cin >> r[i];

    atcoder::mf_graph<int> f(m + n + 2);
    int s = m + n, t = s + 1;
    for(int i = 0; i < m; ++i) f.add_edge(s, i, 1);
    for(int j = m; j < m + n; ++j) f.add_edge(j, t, 1);
    for(int i = 0; i < m; ++i){
        for(int j = 0; j < n; ++j){
            if(gcd(b[i], r[j]) != 1){
                f.add_edge(i, m + j, 1);
            }
        }
    }
    cout << f.flow(s, t) << endl;

    return true;
}

int main(){
    while(1) if(!solve()) break;
}
accuracy: 1
time_ms: 640
memory_kb: 8456
final_rank: 8

code_snippet: //
// Hopcroft-Karp の最大二部マッチング
//
// verified
//   AOJ 1163 カードゲーム
//     http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1163&lang=jp
//
//   TTPC 2022 H - Colorful Graph
//     https://atcoder.jp/contests/ttpc2022/tasks/ttpc2022_h
//


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


struct HopcroftKarp {
    // input
    int size_left, size_right;
    vector<vector<int>> list; // left to right

    // results
    vector<int> lr, rl;
    
    // intermediate results
    vector<bool> seen, matched;
    vector<int> level;
    
    // constructor
    HopcroftKarp(int l, int r) : size_left(l), size_right(r), list(l, vector<int>()) { }
    void add_edge(int from, int to) {
        list[from].push_back(to);
    }

    // getter, debugger
    const vector<int> &operator [] (int i) const { 
        return list[i];
    }
    friend ostream& operator << (ostream& s, const HopcroftKarp& G) {
        s << endl;
        for (int i = 0; i < G.list.size(); ++i) {
            s << i << ": ";
            for (int j = 0; j < G.list[i].size(); ++j) {
                s << G.list[i][j];
                if (j + 1 != G.list[i].size()) s << ", ";
            }
            s << endl;
        }
        return s;
    }
    
    // solver
    void hobfs() {
        queue<int> que;
        for (int left = 0; left < size_left; ++left) {
            level[left] = -1;
            if (!matched[left]) {
                que.push(left);
                level[left] = 0;
            }
        }
        level[size_left] = size_left;
        while (!que.empty()) {
            int left = que.front();
            que.pop();
            for (int i = 0; i < list[left].size(); ++i) {
                int right = list[left][i];
                int next = rl[right];
                if (level[next] == -1) {
                    level[next] = level[left] + 1;
                    que.push(next);
                }
            }
        }
    }
    bool hodfs(int left) {
        if (left == size_left) return true;
        if (seen[left]) return false;
        seen[left] = true;
        for (int i = 0; i < list[left].size(); ++i) {
            int right = list[left][i];
            int next = rl[right];
            if (next == -1) next = size_left;
            if (level[next] > level[left] && hodfs(next)) {
                rl[right] = left;
                return true;
            }
        }
        return false;
    }
    int solve() {
        seen.assign(size_left, false);
        matched.assign(size_left, false);
        level.assign(size_left + 1, -1);
        lr.assign(size_left, -1);
        rl.assign(size_right, -1);
        int res = 0;
        while (true) {
            hobfs();
            seen.assign(size_left, false);
            bool finished = true;
            for (int left = 0; left < size_left; ++left) {
                if (!matched[left] && hodfs(left)) {
                    matched[left] = true;
                    ++res;
                    finished = false;
                }
            }
            if (finished) break;
        }
        for (int r = 0; r < size_right; r++) {
            if (rl[r] != -1) lr[rl[r]] = r;
        }
        return res;
    }
};


//------------------------------//
// Examples
//------------------------------//

// AOJ 1163 カードゲーム
void AOJ_1163() {
    int N, M;
    while (cin >> N >> M, N) {
        HopcroftKarp G(N, M);
        vector<int> left(N), right(M);
        for (int i = 0; i < N; ++i) cin >> left[i];
        for (int i = 0; i < M; ++i) cin >> right[i];
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < M; ++j) {
                if (gcd(left[i], right[j]) > 1) G.add_edge(i, j);
            }
        }
        cout << G.solve() << endl;
    }
}

// TTPC 2022 H - Colorful Graph
// Edge Class
template<class T> struct Edge {
    int from, to;
    T val;
    Edge() : from(-1), to(-1), val(-1) { }
    Edge(int f, int t, T v = -1) : from(f), to(t), val(v) {}
    friend ostream& operator << (ostream& s, const Edge& E) {
        return s << E.from << "->" << E.to;
    }
};

// graph class
template<class T> struct Graph {
    vector<vector<Edge<T>>> list;
    vector<vector<Edge<T>>> reversed_list;
    
    Graph(int n = 0) : list(n), reversed_list(n) { }
    void init(int n = 0) {
        list.assign(n, vector<Edge<T>>());
        reversed_list.assign(n, vector<Edge<T>>());
    }
    const vector<Edge<T>> &operator [] (int i) const { return list[i]; }
    const vector<Edge<T>> &get_rev_edges(int i) const { return reversed_list[i]; }
    const size_t size() const { return list.size(); }
        
    void add_edge(int from, int to, T val = -1) {
        list[from].push_back(Edge(from, to, val));
        reversed_list[to].push_back(Edge(to, from, val));
    }
    
    void add_bidirected_edge(int from, int to, T val = -1) {
        list[from].push_back(Edge(from, to, val));
        list[to].push_back(Edge(to, from, val));
        reversed_list[from].push_back(Edge(from, to, val));
        reversed_list[to].push_back(Edge(to, from, val));
    }

    friend ostream &operator << (ostream &s, const Graph &G) {
        s << endl;
        for (int i = 0; i < G.size(); ++i) {
            s << i << " -> ";
            for (const auto &e : G[i]) s << e.to << " ";
            s << endl;
        }
        return s;
    }
};

// strongly connected components decomposition
template<class T> struct SCC {
    // results
    vector<int> cmp;
    vector<vector<int>> groups;
    Graph<T> dag;
    
    // intermediate results
    vector<bool> seen;
    vector<int> vs, rvs;
    
    // constructor
    SCC() { }
    SCC(const Graph<T> &G) { 
        solve(G);
    }
    void init(const Graph<T> &G) { 
        solve(G);
    }

    // getter, compressed dag（v: node-id of compressed dag)
    int get_size(int v) const {
        return groups[v].size();
    }
    vector<int> get_group(int v) const {
        return groups[v];
    }

    // solver
    void dfs(const Graph<T> &G, int v) {
        seen[v] = true;
        for (const auto &e : G[v]) if (!seen[e.to]) dfs(G, e.to);
        vs.push_back(v);
    }
    void rdfs(const Graph<T> &G, int v, int k) {
        seen[v] = true;
        cmp[v] = k;
        for (const auto &e : G.get_rev_edges(v)) if (!seen[e.to]) rdfs(G, e.to, k);
        rvs.push_back(v);
    }
    void reconstruct(const Graph<T> &G) {
        dag.init((int)groups.size());
        set<pair<int,int>> new_edges;
        for (int i = 0; i < (int)G.size(); ++i) {
            int u = cmp[i];
            for (const auto &e : G[i]) {
                int v = cmp[e.to];
                if (u == v) continue;
                if (!new_edges.count({u, v})) {
                    dag.add_edge(u, v);
                    new_edges.insert({u, v});
                }
            }
        }
    }
    void solve(const Graph<T> &G) {
        // first dfs
        seen.assign((int)G.size(), false);
        vs.clear();
        for (int v = 0; v < (int)G.size(); ++v) if (!seen[v]) dfs(G, v);

        // back dfs
        int k = 0;
        groups.clear();
        seen.assign((int)G.size(), false);
        cmp.assign((int)G.size(), -1);
        for (int i = (int)G.size()-1; i >= 0; --i) {
            if (!seen[vs[i]]) {
                rvs.clear();
                rdfs(G, vs[i], k++);
                groups.push_back(rvs);
            }
        }
        reconstruct(G);
    }
};
void TTPC_2022_H() {
    int N, M, a, b;
    cin >> N >> M;
    Graph<int> G(N);
    for (int i = 0; i < M; i++) {
        cin >> a >> b;
        a--, b--;
        G.add_edge(a, b, i);
    }
    SCC scc(G);

    const auto &dag = scc.dag;
    const auto &groups = scc.groups;

    int V = dag.size();
    HopcroftKarp hk(V, V);
    for (int v = 0; v < V; v++) {
        vector<bool> can(V, false);
        can[v] = true;
        queue<int> que;
        que.push(v);
        while (!que.empty()) {
            auto x = que.front();
            que.pop();
            for (auto e : dag[x]) {
                if (can[e.to]) continue;
                can[e.to] = true;
                que.push(e.to);
            }
        }
        for (int v2 = 0; v2 < V; v2++) {
            if (v2 != v && can[v2]) hk.add_edge(v, v2);
        }
    }
    int max_flow = hk.solve();

    vector<int> source, ans(V, -1);
    for (int v = 0; v < V; v++) if (hk.rl[v] == -1) source.push_back(v);
    for (int c = 0; c < source.size(); c++) {
        int v = source[c];
        while (v != -1) {
            ans[v] = c + 1;
            v = hk.lr[v];
        }
    }

    vector<int> res(N, -1);
    for (int v = 0; v < V; v++) for (auto v2 : groups[v]) res[v2] = ans[v];
    for (int i = 0; i < N; i++) cout << res[i] << " ";
    cout << endl;
}


int main() {
    AOJ_1163();
    //TTPC_2022_H();
}
accuracy: 1
time_ms: 590
memory_kb: 4108
final_rank: 2

code_snippet: #include <bits/stdc++.h>
#include <numeric>
#define LOOP(n) for (int _i = 0; _i < (n); _i++)
#define REP(i, n) for (int i = 0; i < (n); ++i)
#define RREP(i, n) for (int i = (n); i >= 0; --i)
#define FOR(i, r, n) for (int i = (r); i < (n); ++i)
#define ALL(obj) begin(obj), end(obj)
#define yes "Yes"
#define no "No"
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ll,int> pli;
typedef pair<int,ll> pil;
typedef vector<vector<int>> vvi;
typedef vector<vector<ll>> vvl;
const vector<char> alpha = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
const ll INF = 1000000007;
const ll MOD = 998244353;
#define mpair(a,b) make_pair((a),(b))
template<typename T> inline bool chmax(T &a, T b) { return ((a < b) ? (a = b, true) : (false)); }
template<typename T> inline bool chmin(T &a, T b) { return ((a > b) ? (a = b, true) : (false)); }
struct MaximumFlow{
    struct edge{
        int from,to;
        ll cap, flow;
        int rev;
    };
    int n;
    vector<vector<edge>> G;
    MaximumFlow(int n) : n(n),G(n){}
    void add_edge(int s,int t,ll cap){//対になる辺がどこにあるかを記録
        G[s].push_back({s,t,cap,0,(int)G[t].size()});
        G[t].push_back({t,s,0,0,(int)G[s].size()-1});
    }

    ll max_flow(int s,int t){
        vector<int> dist(n), iter(n);
        function<int(void)> BFS = [&](void) -> int{
            dist.assign(n,-1);
            dist[s]=0;
            queue<int> que;
            que.push(s);
            while(!que.empty()){
                int v=que.front();
                que.pop();
                for(auto &e:G[v]) if (e.cap>e.flow&&dist[e.to]==-1){
                    que.push(e.to);
                    dist[e.to]=dist[v]+1;
                }
            }
            return dist[t];
        };
        function<ll(int,ll)> DFS = [&](int u,ll cur){
            if (u==t) return cur;
            for(int &i=iter[u];i<G[u].size();i++){
                edge &e=G[u][i], &re=G[e.to][e.rev];
                if (e.cap>e.flow && dist[u]<dist[e.to]){
                    ll f=DFS(e.to,min(cur,e.cap-e.flow));
                    if (f>0){
                        e.flow+=f;
                        re.flow-=f;
                        return f;
                    }
                }
            }
            return 0LL;
        };

        REP(u,n) for(auto &e:G[u]) e.flow=0;
        ll flow=0;
        while(BFS()>=0){
            iter.assign(n,0);
            while(true){
                ll res=DFS(s,1e18);
                if (res<=0) break;
                flow+=res;
            }
        }
        return flow;
    }
};

int p(int a,int b){
    return __gcd(a,b);
}
void solve(){
    int M,N;
    cin >> M >> N;
    if (N==0&&M==0) exit(0);
    vector<int> A(M),B(N);
    REP(i,M) cin >> A[i];
    REP(i,N) cin >> B[i];
    MaximumFlow MF(N+M+2);

    REP(i,M) REP(j,N) {
        int res=p(A[i],B[j]);
        if (res>1) MF.add_edge(i,M+j,1);
    }

    REP(i,M) MF.add_edge(N+M,i,1);
    REP(j,N) MF.add_edge(M+j,N+M+1,1);

    ll mx=MF.max_flow(N+M,N+M+1);
    cout << mx << endl;

}
int main(){
    while(true) solve();
}
accuracy: 1
time_ms: 650
memory_kb: 13908
final_rank: 15

code_snippet: #ifndef ONLINE_JUDGE
#define _GLIBCXX_DEBUG 1 //[]で配列外参照をするとエラーにしてくれる。上下のやつがないとTLEになるので注意 ABC311Eのサンプル4みたいなデバック中のTLEは防げないので注意
#endif
#include <bits/stdc++.h>

namespace atcoder {

namespace internal {

template <class T> struct simple_queue {
    std::vector<T> payload;
    int pos = 0;
    void reserve(int n) { payload.reserve(n); }
    int size() const { return int(payload.size()) - pos; }
    bool empty() const { return pos == int(payload.size()); }
    void push(const T& t) { payload.push_back(t); }
    T& front() { return payload[pos]; }
    void clear() {
        payload.clear();
        pos = 0;
    }
    void pop() { pos++; }
};

}  // namespace internal

}  // namespace atcoder
namespace atcoder {

template <class Cap> struct mf_graph {
  public:
    mf_graph() : _n(0) {}
    explicit mf_graph(int n) : _n(n), g(n) {}

    int add_edge(int from, int to, Cap cap) {
        assert(0 <= from && from < _n);
        assert(0 <= to && to < _n);
        assert(0 <= cap);
        int m = int(pos.size());
        pos.push_back({from, int(g[from].size())});
        int from_id = int(g[from].size());
        int to_id = int(g[to].size());
        if (from == to) to_id++;
        g[from].push_back(_edge{to, to_id, cap});
        g[to].push_back(_edge{from, from_id, 0});
        return m;
    }

    struct edge {
        int from, to;
        Cap cap, flow;
    };

    edge get_edge(int i) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        auto _e = g[pos[i].first][pos[i].second];
        auto _re = g[_e.to][_e.rev];
        return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
    }
    std::vector<edge> edges() {
        int m = int(pos.size());
        std::vector<edge> result;
        for (int i = 0; i < m; i++) {
            result.push_back(get_edge(i));
        }
        return result;
    }
    void change_edge(int i, Cap new_cap, Cap new_flow) {
        int m = int(pos.size());
        assert(0 <= i && i < m);
        assert(0 <= new_flow && new_flow <= new_cap);
        auto& _e = g[pos[i].first][pos[i].second];
        auto& _re = g[_e.to][_e.rev];
        _e.cap = new_cap - new_flow;
        _re.cap = new_flow;
    }

    Cap flow(int s, int t) {
        return flow(s, t, std::numeric_limits<Cap>::max());
    }
    Cap flow(int s, int t, Cap flow_limit) {
        assert(0 <= s && s < _n);
        assert(0 <= t && t < _n);
        assert(s != t);

        std::vector<int> level(_n), iter(_n);
        internal::simple_queue<int> que;

        auto bfs = [&]() {
            std::fill(level.begin(), level.end(), -1);
            level[s] = 0;
            que.clear();
            que.push(s);
            while (!que.empty()) {
                int v = que.front();
                que.pop();
                for (auto e : g[v]) {
                    if (e.cap == 0 || level[e.to] >= 0) continue;
                    level[e.to] = level[v] + 1;
                    if (e.to == t) return;
                    que.push(e.to);
                }
            }
        };
        auto dfs = [&](auto self, int v, Cap up) {
            if (v == s) return up;
            Cap res = 0;
            int level_v = level[v];
            for (int& i = iter[v]; i < int(g[v].size()); i++) {
                _edge& e = g[v][i];
                if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
                Cap d =
                    self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
                if (d <= 0) continue;
                g[v][i].cap += d;
                g[e.to][e.rev].cap -= d;
                res += d;
                if (res == up) return res;
            }
            level[v] = _n;
            return res;
        };

        Cap flow = 0;
        while (flow < flow_limit) {
            bfs();
            if (level[t] == -1) break;
            std::fill(iter.begin(), iter.end(), 0);
            Cap f = dfs(dfs, t, flow_limit - flow);
            if (!f) break;
            flow += f;
        }
        return flow;
    }

    std::vector<bool> min_cut(int s) {
        std::vector<bool> visited(_n);
        internal::simple_queue<int> que;
        que.push(s);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            visited[p] = true;
            for (auto e : g[p]) {
                if (e.cap && !visited[e.to]) {
                    visited[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        return visited;
    }

  private:
    int _n;
    struct _edge {
        int to, rev;
        Cap cap;
    };
    std::vector<std::pair<int, int>> pos;
    std::vector<std::vector<_edge>> g;
};

}  // namespace atcoder

using namespace atcoder;

using namespace std;
using ll = long long;
ll INF = 2e18;
using P = pair<ll, ll>;
#define pb push_back
#define rep(i, n) for (ll i = 0; i < (n); i++)
#define reprev(i, n) for (ll i = (n) - 1; i >= 0; i--)
#define reps(i, n) for (ll i = 1; i <= (n); i++)
#define for_(i, a, b) for (ll i = (a); i < (b); i++)
#define all(v) v.begin(), v.end()
template <typename T>
inline bool chmin(T &a, const T &b)
{
  bool c = a > b;
  if (c)
    a = b;
  return c;
}
template <typename T>
inline bool chmax(T &a, const T &b)
{
  bool c = a < b;
  if (c)
    a = b;
  return c;
}
template <typename T>
inline T ceil(T a, T b) { return (a + (b - 1)) / b; }
// using mint = modint998244353;
//  using mint = modint1000000007;
//  using mint = static_modint<10>;//使うときはコメントアウトを外す
template <typename T>
using vc = vector<T>;
template <class T>
istream &operator>>(istream &i, vc<T> &v)
{
  rep(j, size(v)) i >> v[j];
  return i;
}
template <class T>
ostream &operator<<(ostream &o, const vc<T> &v)
{
  rep(j, size(v))
  {
    if (j)
      o << " ";
    o << v[j];
  }
  o << endl;
  return o;
}
int main()
{
  cin.tie(nullptr);
  ios_base::sync_with_stdio(false);
  // ref:https://rsk0315.hatenablog.com/entry/2020/05/09/170315
  while (true)
  {
    ll M, N;
    cin >> M >> N;
    if (N == 0 && M == 0)
      break;
    vc<ll> a(M), b(N);
    cin >> a >> b;
    ll s = M + N;
    ll t = M + N + 1;
    mf_graph<ll> g(M + N + 2);
    rep(i, M)
    {
      g.add_edge(s, i, 1);
    }
    rep(i, N)
    {
      g.add_edge(M + i, t, 1);
    }
    rep(i, M)
    {
      rep(j, N)
      {
        if (gcd(a[i], b[j]) > 1)
        {
          g.add_edge(i, M + j, 1);
        }
      }
    }
    cout << g.flow(s, t) << endl;
  }
  //  cout << ans << endl;
  return 0;
}
accuracy: 1
time_ms: 980
memory_kb: 9928
final_rank: 12

code_snippet: #line 1 "/home/dispersion/competitive/icpc/ICPC-library/ICPC2024/merge_src_docs/template/24_template.hpp"
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
#define rep(i, n) for(ll i = 0; i < n; i++)
#define rep2(i, l, r) for(ll i = l; i < r; i++)

using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;

#define all(A) A.begin(), A.end()
#define elif else if
using pii = pair<ll, ll>;

bool chmin(auto &a, auto b) { return a > b ? a = b, 1 : 0; }
bool chmax(auto &a, auto b) { return a < b ? a = b, 1 : 0; }

struct IOSetup {
  IOSetup() {
    cin.tie(0);
    ios::sync_with_stdio(0);
  }
} iosetup;

template<class T>
void print(vector<T> a) {
  for(auto x : a) cout << x << ' ';
  cout << endl;
}

void print(auto x) { cout << x << endl; }

template<class Head, class... Tail>
void print(Head &&head, Tail &&...tail) {
  cout << head << ' ';
  print(forward<Tail>(tail)...);
}
#line 1 "/home/dispersion/competitive/icpc/ICPC-library/ICPC2024/merge_src_docs/graph/Hungarian.hpp"
/*
N x N$ 行列 A に対して sum A[i][P[i]] (1 <= i <= N) を最小化する順列 P を返す
最大化したい場合は各要素を -1 倍する
*/

vi hungarian(vector<vll> a) {
  ll inf = 1LL << 60;
  int n = (int)a.size();
  vi p(n);
  rep(i, n) p[i] = i;
  vll h = {0};

  rep2(i, 1, n) {
    h.push_back(0);
    vll d(i + 1, inf);
    vi pre(i + 1, -1), used(i + 1, 0);
    d[i] = 0;
    pre[i] = i;

    rep(_, i + 1) {
      ll min_d = inf;
      int v = -1;
      for(int j = 0; j <= i; j++) {
        if(used[j] == 0 && min_d > d[j] - h[j]) {
          min_d = d[j] - h[j];
          v = j;
        }
      }
      used[v] = 1;
      rep(j, i + 1) {
        if(used[j] == 0 || j == i) {
          ll nd = d[v] - a[v][p[v]] + a[j][p[v]];
          if(d[j] > nd) {
            d[j] = nd;
            pre[j] = v;
          }
        }
      }
    }

    int cur = i;
    while(pre[cur] != i) {
      swap(p[cur], p[pre[cur]]);
      cur = pre[cur];
    }
    h = d;
  }
  return p;
}
#line 3 "Hungarian.cpp"

#line 1 "/home/dispersion/competitive/icpc/ICPC-library/ICPC2024/merge_src_docs/math/binary_gcd.hpp"
ll binary_gcd(ll x_, ll y_) {
  unsigned long long x = (x_ < 0 ? -x_ : x_), y = (y_ < 0 ? -y_ : y_);
  if (!x or !y) return x + y; // x == 0 or y == 0
  int n = countr_zero(x), m = countr_zero(y);
  x >>= n, y >>= m;
  while (x != y) {
    if (x > y) {
      x = (x - y) >> countr_zero(x - y);
    } else {
      y = (y - x) >> countr_zero(y - x);
    }
  }
  return x << (n > m ? m : n);
}
#line 5 "Hungarian.cpp"

// https://onlinejudge.u-aizu.ac.jp/services/ice/?problemId=1163
void solve1() {
  while(1) {
    ll N, M; cin >> N >> M;
    if(N + M == 0) break;
    
    vector<ll> B(N), R(M);
    for(auto &b : B) cin >> b;
    for(auto &r : R) cin >> r;
    
    ll C = max(N, M);
    vector mat(C, vector<ll>(C, 0));
    rep(i, N) {
      rep(j, M) {
        ll b = B[i], r = R[j];
        if(binary_gcd(b, r) > 1) mat[i][j] = -1;
      }
    }
    
    vector<int> P = hungarian(mat);
    ll res = 0;
    rep(i, N) res += mat[i][P[i]];
    res *= (-1);
    cout << res << '\n';
  }
  
  return;
}

int main() {
  solve1();
  
  return 0;
}
accuracy: 1
time_ms: 1990
memory_kb: 7680
final_rank: 13

code_snippet: #include <bits/stdc++.h>
#include "atcoder/all"
using namespace std;
#define rep(i, n) for (long long i = 0; i < (long long)(n); i++)
#define FOR(i, s, e) for (long long i = (long long)(s); i <= (long long)(e); i++)
#define printYesNo(is_ok) puts(is_ok ? "Yes" : "No");
#define SORT(v) sort(v.begin(), v.end());
#define RSORT(v) sort(v.rbegin(), v.rend());
#define REVERSE(v) reverse(v.begin(), v.end());

template <typename T>
void printlnVector(T v)
{
    rep(i, v.size())
    {
        cout << v[i] << endl;
    }
}

template <typename T>
void printVector(T v)
{
    for (long unsigned int i = 0; i < v.size(); i++)
    {
        cout << v[i];
        if (i != v.size() - 1)
            cout << " ";
    }
    cout << endl;
}

void solve(int m, int n, vector<int> b, vector<int> r)
{
    atcoder::mf_graph<int> graph(m + n + 2);
    rep(b_i, m) rep(r_i, n)
    {
        int gcd_n = gcd(b[b_i], r[r_i]);
        if (gcd_n == 1)
        {
            continue;
        }

        graph.add_edge(b_i, m + r_i, 1);
    }

    rep(b_i, m)
    {
        graph.add_edge(m + n, b_i, 1);
    }

    rep(r_i, n)
    {
        graph.add_edge(m + r_i, m + n + 1, 1);
    }

    int ans = graph.flow(m + n, m + n + 1);
    cout << ans << endl;
}

int main()
{

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

        vector<int> b(m), r(n);
        rep(i, m)
        {
            cin >> b[i];
        }
        rep(i, n)
        {
            cin >> r[i];
        }
        solve(m, n, b, r);
    }

    return 0;
}
accuracy: 1
time_ms: 420
memory_kb: 8732
final_rank: 5

code_snippet: #include <bits/stdc++.h>
    using namespace std;
    
    template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return 1; } return 0; }
    template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return 1; } return 0; }
    #define rep(i,n) for (int i = 0; i < (n); ++i)

    typedef long long ll;
    typedef unsigned long long ull;
    using P=pair<ll,ll>;
    using T=tuple<int,int,int>;
    const int INF=1001001001;
    const ll INFL=1e18;
    const int mod=998244353;

	struct edge{
		int to,cap,rev;
	};
	
	const int MAX_V=200005;
	vector<edge>G[MAX_V];
	bool used[MAX_V];
	
	void add_edge(int from,int to,int cap){
		G[from].push_back((edge){to,cap,(int)G[to].size()});
		G[to].push_back((edge){from,0,(int)G[from].size()-1});
	}

	int dfs(int v,int t,int f){
		if(v==t){return f;}
		used[v]=true;
		for(int i=0;i<G[v].size();i++){
			edge &e=G[v][i];
			if(!used[e.to]&&e.cap>0){
				int d=dfs(e.to,t,min(f,e.cap));
				if(d>0){
					e.cap-=d;
					G[e.to][e.rev].cap+=d;
					return d;
				}
			}
		}
		return 0;
	}

	int max_flow(int s,int t){
		int flow=0;
		while(1){
			memset(used,0,sizeof(used));
			int f=dfs(s,t,INF);
			if(f==0){return flow;}
			flow+=f;
		}
	}

    bool solve(){
		int m,n;
		cin>>m>>n;
		if(!n)return false;
		rep(i,n+m+5){
			G[i]=vector<edge>();
			used[i]=0;
		}

		vector<int>b(m),r(n);
		rep(i,m)cin>>b[i];
		rep(i,n)cin>>r[i];

		int s=n+m,t=n+m+1;
		rep(i,m){
			add_edge(s,i,1);
			rep(j,n){
				if(__gcd(b[i],r[j])>1){
					add_edge(i,j+m,1);
				}
			}
		}
		rep(i,n){
			add_edge(i+m,t,1);
		}

		int ans=max_flow(s,t);
		cout<<ans<<endl;
		return true;
    }

    int main(){
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);

		while(1){
			if(!solve())break;
		}
		
        return 0;
    }
accuracy: 1
time_ms: 660
memory_kb: 12688
final_rank: 14

code_snippet: #include <bits/stdc++.h>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <math.h>
#include <random>
#include <chrono>
#include <cstdint>


using namespace std;
using vi = vector<int>;
using vvi = vector<vector<int>>;
using vl = vector<long long>;
using vvl = vector<vector<long long>>;
using ll = long long;
template <class T> using max_heap = priority_queue<T>;
template <class T> using min_heap = priority_queue<T, vector<T>, greater<>>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define rep2(i, f, n) for (int i = (int) f; i < (int)(n); i++)
#define repd(i, n, l) for (int i = (int) n; i >= (int) l; i--)
#define all(p) p.begin(),p.end()
vector<pair<int, int>> dydx{{-1, 0}, {1, 0}, {0, -1 }, {0, 1}};
const ll inf = 1LL << 60;
void print(){ putchar(' '); }
void print(bool a){ printf("%d", a); }
void print(int a){ printf("%d", a); }
void print(unsigned a){ printf("%u", a); }
void print(long a){ printf("%ld", a); }
void print(long long a){ printf("%lld", a); }
void print(unsigned long long a){ printf("%llu", a); }
void print(char a){ printf("%c", a); }
void print(char a[]){ printf("%s", a); }
void print(const char a[]){ printf("%s", a); }
void print(float a){ printf("%.15f", a); }
void print(double a){ printf("%.15f", a); }
void print(long double a){ printf("%.15Lf", a); }
void print(const string& a){ for(auto&& i : a) print(i); }
template<class T> void print(const complex<T>& a){ if(a.real() >= 0) print('+'); print(a.real()); if(a.imag() >= 0) print('+'); print(a.imag()); print('i'); }
template<class T> void print(const vector<T>&);
template<class T, size_t size> void print(const array<T, size>&);
template<class T, class L> void print(const pair<T, L>& p);
template<class T, size_t size> void print(const T (&)[size]);
template<class T> void print(const vector<T>& a){ if(a.empty()) return; print(a[0]); for(auto i = a.begin(); ++i != a.end(); ){ putchar(' '); print(*i); } }
template<class T> void print(const deque<T>& a){ if(a.empty()) return; print(a[0]); for(auto i = a.begin(); ++i != a.end(); ){ putchar(' '); print(*i); } }
template<class T, size_t size> void print(const array<T, size>& a){ print(a[0]); for(auto i = a.begin(); ++i != a.end(); ){ putchar(' '); print(*i); } }
template<class T, class L> void print(const pair<T, L>& p){ print(p.first); putchar(' '); print(p.second); }
template<class T, size_t size> void print(const T (&a)[size]){ print(a[0]); for(auto i = a; ++i != end(a); ){ putchar(' '); print(*i); } }
template<class T> void print(const T& a){ cout << a; }

// https://ei1333.github.io/luzhiled/snippets/math/matrix.html
#include <vector>
#include <queue>

template< class T >
struct Matrix {
  vector< vector< T > > A;

  Matrix() {}

  Matrix(size_t n, size_t m) : A(n, vector< T >(m, 0)) {}

  Matrix(size_t n) : A(n, vector< T >(n, 0)) {};

  size_t height() const {
    return (A.size());
  }

  size_t width() const {
    return (A[0].size());
  }

  inline const vector< T > &operator[](int k) const {
    return (A.at(k));
  }

  inline vector< T > &operator[](int k) {
    return (A.at(k));
  }

  static Matrix I(size_t n) {
    Matrix mat(n);
    for(int i = 0; i < n; i++) mat[i][i] = 1;
    return (mat);
  }

  Matrix &operator+=(const Matrix &B) {
    size_t n = height(), m = width();
    assert(n == B.height() && m == B.width());
    for(int i = 0; i < n; i++)
      for(int j = 0; j < m; j++)
        (*this)[i][j] += B[i][j];
    return (*this);
  }

  Matrix &operator-=(const Matrix &B) {
    size_t n = height(), m = width();
    assert(n == B.height() && m == B.width());
    for(int i = 0; i < n; i++)
      for(int j = 0; j < m; j++)
        (*this)[i][j] -= B[i][j];
    return (*this);
  }

  Matrix &operator*=(const Matrix &B) {
    size_t n = height(), m = B.width(), p = width();
    assert(p == B.height());
    vector< vector< T > > C(n, vector< T >(m, 0));
    for(int i = 0; i < n; i++)
      for(int j = 0; j < m; j++)
        for(int k = 0; k < p; k++)
          C[i][j] = (C[i][j] + (*this)[i][k] * B[k][j]);
    A.swap(C);
    return (*this);
  }

  Matrix &operator^=(long long k) {
    Matrix B = Matrix::I(height());
    while(k > 0) {
      if(k & 1) B *= *this;
      *this *= *this;
      k >>= 1LL;
    }
    A.swap(B.A);
    return (*this);
  }

  Matrix operator+(const Matrix &B) const {
    return (Matrix(*this) += B);
  }

  Matrix operator-(const Matrix &B) const {
    return (Matrix(*this) -= B);
  }

  Matrix operator*(const Matrix &B) const {
    return (Matrix(*this) *= B);
  }

  Matrix operator^(const long long k) const {
    return (Matrix(*this) ^= k);
  }

  friend ostream &operator<<(ostream &os, Matrix &p) {
    size_t n = p.height(), m = p.width();
    for(int i = 0; i < n; i++) {
      os << "[";
      for(int j = 0; j < m; j++) {
        os << p[i][j] << (j + 1 == m ? "]\n" : ",");
      }
    }
    return (os);
  }


  T determinant() {
    Matrix B(*this);
    assert(width() == height());
    T ret = 1;
    for(int i = 0; i < width(); i++) {
      int idx = -1;
      for(int j = i; j < width(); j++) {
        if(B[j][i] != 0) idx = j;
      }
      if(idx == -1) return (0);
      if(i != idx) {
        ret *= -1;
        swap(B[i], B[idx]);
      }
      ret *= B[i][i];
      T vv = B[i][i];
      for(int j = 0; j < width(); j++) {
        B[i][j] /= vv;
      }
      for(int j = i + 1; j < width(); j++) {
        T a = B[j][i];
        for(int k = 0; k < width(); k++) {
          B[j][k] -= B[i][k] * a;
        }
      }
    }
    return (ret);
  }
};

template<typename T>
T Hungarian(Matrix<T> &C){
    const T infty = numeric_limits<T>::max();
    const int N = C.height();
    const int M = C.width();
    vector<int> P(M), way(M);
    vector<T> F(N, 0), G(M, 0), minV;
    vector<bool> used;
    for (int i = 1 ; i < N; i++){
        used.assign(M, false);
        minV.assign(M, infty);
        P[0] = i;
        int j0 = 0;
        while (P[j0] != 0){
            int i0 = P[j0];
            int j1 = 0;
            used[j0] = true;
            T delta = infty;
            for (int j = 1; j < M; j++){
                if (used[j]) continue;
                T curr = C[i0][j] - F[i0] - G[j];
                if (curr < minV[j]) minV[j] = curr, way[j] = j0;
                if (minV[j] < delta) delta = minV[j], j1 = j;
            }
            for (int j = 0; j < M; j++){
                if (used[j]) F[P[j]] += delta, G[j] -= delta;
                else minV[j] -= delta;
            }
            j0 = j1;
        }
        do {
            P[j0] = P[way[j0]];
            j0 = way[j0];
        } while (j0 != 0);
    }
    // print(G);cout << endl;
    return -G[0];
}

vector<vector<int>> unweighted_graph(int n, int m){
    vector<vector<int>> ret(n);
    while (m--){
        int a, b;
        cin >> a >> b;
        a--; b--;
        ret[a].push_back(b);
        ret[b].push_back(a);
    }
    return ret;
}
vector<vector<pair<int, long long>>> weighted_graph(int n, int m){
    vector<vector<pair<int, long long>>> ret(n);
    while (m--){
        int a, b;
        long long c;
        cin >> a >> b >> c;
        a--, b--;
        ret[a].push_back({b, c});
        ret[b].push_back({a, c});
    }
    return ret;
}

template<typename T>
int argmin(vector<T> &a){
    T mi = *min_element(all(a));
    for (int i = 0; i < a.size(); i++){
        if (a[i] == mi) return i;
    }
}

template<typename T>
int argmax(vector<T> &a){
    T ma = *max_element(all(a));
    for (int i = 0; i < a.size(); i++){
        if (a[i] == ma) return i;
    }
}

int main(){
    int B[500], R[500];
    while (1){
        int m, n;
        cin >> m >> n;
        if (!n && !m) break;
        rep(i, m) cin >> B[i];
        rep(i, n) cin >> R[i];
        if (m > n) swap(n, m), swap(B, R);
        Matrix<int> mat(m+1, n+1);
        rep(i, m+1) rep(j, n+1) mat[i][j] = 0;
        rep(i, m) rep(j, n) if (__gcd(B[i], R[j]) > 1) mat[i+1][j+1] = -1;
        // rep(i, m+1) rep(j, n+1) cout << mat[i][j] << ' ';
        cout << -Hungarian(mat) << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 4300
memory_kb: 4128
final_rank: 18

code_snippet: #include<iostream>
#include<string>
#include<queue>
#include<vector>
#include<cassert>
#include<random>
#include<set>
#include<map>
#include<cassert>
#include<unordered_map>
#include<bitset>
#include<algorithm>
#include<numeric>
using namespace std;
using ll = long long;
const int inf=1<<30;
const ll INF=1LL<<62;
using P=pair<int,int>;
using PP=pair<P,int>; 


ll gcd(ll x,ll y){
    return y==0?x:gcd(y,x%y);
}


template<class T,T INF=(1<<30) > class FLOW{
    //intになるかlong long になるか
    public:
    struct edge{int from; int to;  T cap; T flow; int rev;};    
    std::vector<T> level;
    std::vector<T> iter;
    std::vector<std::vector<edge>> G;
    std::set<std::pair<int,int>> st;
    int numV=0;

    //constructor
    FLOW(int V){
        numV=V;
        G = std::vector<std::vector<edge>>(V+1,std::vector<edge>{});
        //G[0],G[1],...,G[V]を作る
        
        level = std::vector<T>(V+1,0);
        iter  = std::vector<T>(V+1,0);
    }

    std::vector<P> edgeIdx;

    void add_edge(int from,int to,T cap){
        // from to cap flow rev
        edgeIdx.emplace_back(std::make_pair(from,(int)G[from].size()));
        G[from].push_back((edge){from,to,cap,0,(int)G[to].size()} );
        st.insert(make_pair(from,to));//順辺のみに興味がある


        G[to].push_back((edge){to,from,0,cap,(int)G[from].size()-1});
    }

    edge get_edge(int idx){
        //idx番目の辺の情報を返す
        //idxは追加された辺のインデックス
        auto [from,i]=edgeIdx[idx];
        return G[from][i];
    }
 
 
    void bfs(int s){
        level = std::vector<T>(numV+1,-1);//-1に初期化

        std::queue<int> que;
        level[s]=0;
        que.push(s);
        while(!que.empty()){
    
            int p = que.front();que.pop();
            for(int i=0;i<(int)G[p].size();i++){
                edge e = G[p][i];//頂点pに繋がっている頂点が e.to
                if(e.cap>0 && level[e.to]<0){
                    level[e.to] = level[p]+1;
                    que.push(e.to);
                }
            }
        }
    }
 
    T dfs(int v,int t,T f){
        if(v==t) return f;
        for(T &i=iter[v]; i<(int)G[v].size(); i++){
            edge &e = G[v][i];
            if(e.cap>0 && level[v]<level[e.to]){
                T d = dfs(e.to, t, min(f,e.cap));
                if(d>0){
                    e.cap -= d;
                    e.flow += d;//capが減った分,今,流す量が増えた


                    G[e.to][e.rev].cap += d;
                    G[e.to][e.rev].flow -= d;//逆辺を考えた分,今流している量が減った.
                    return d;
                }
            }
        }
 
        return 0;
    }
 
 
    T max_flow(int s, int t){
        T flow=0;
        while(1){
            bfs(s);
            if(level[t]<0) return flow;
            iter = vector<T>(numV+1,0);//0に初期化

            T f;
            while((f=dfs(s,t,INF))>0 ){
                flow += f;
            }
        }
    }

    //auto edges = (FLOW instance).edges();で受けるしかないかな
    std::vector<edge> edges(){
        std::vector<edge> ret;
        for(int from=0;from<=numV;from++){
            for(const edge &e:G[from]){
                if( st.find({from,e.to})!=st.end() ){
                    ret.push_back(e);
                }
            }
        }
        return ret;
    }

};

int main(){


    vector<int> output;
    while(1){
        int m,n;
        cin>>m>>n;
        if(m==0 && n==0)break;
        vector<ll> b(m),r(n);
        for(int i=0;i<m;i++){
            cin>>b[i];
        }
        for(int i=0;i<n;i++){
            cin>>r[i];
        }

        FLOW<int> fl(m+n+2);
        int source=m+n;
        int sink=m+n+1;
        for(int i=0;i<m;i++){
            fl.add_edge(source,i,1);
        }
        for(int i=0;i<n;i++){
            fl.add_edge(i+m,sink,1);
        }

        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(gcd(b[i],r[j])>1){
                    fl.add_edge(i,j+m,1);
                }
            }
        }

        
        int ans=fl.max_flow(source,sink);
        output.push_back(ans);
    }
    for(int v:output){
        cout<<v<<endl;
    }
}
accuracy: 1
time_ms: 1130
memory_kb: 16784
final_rank: 20

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

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

#include <unistd.h>

namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf


uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio


using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */

namespace Random {
mt19937_64 randgen(chrono::steady_clock::now().time_since_epoch().count());
using u64 = unsigned long long;
u64 get() {
    return randgen();
}
template <typename T> T get(T L) { // [0,L]

    return get() % (L + 1);
}
template <typename T> T get(T L, T R) { // [L,R]

    return get(R - L) + L;
}
double uniform() {
    return double(get(1000000000)) / 1000000000;
}
string str(int n) {
    string ret;
    rep(i, 0, n) ret += get('a', 'z');
    return ret;
}
template <typename Iter> void shuffle(Iter first, Iter last) {
    if (first == last)
        return;
    int len = 1;
    for (auto it = first + 1; it != last; it++) {
        len++;
        int j = get(0, len - 1);
        if (j != len - 1)
            iter_swap(it, first + j);
    }
}
template <typename T> vector<T> select(int n, T L, T R) { // [L,R]

    if (n * 2 >= R - L + 1) {
        vector<T> ret(R - L + 1);
        iota(ALL(ret), L);
        shuffle(ALL(ret));
        ret.resize(n);
        return ret;
    } else {
        unordered_set<T> used;
        vector<T> ret;
        while (SZ(used) < n) {
            T x = get(L, R);
            if (!used.count(x)) {
                used.insert(x);
                ret.push_back(x);
            }
        }
        return ret;
    }
}

void relabel(int n, vector<pair<int, int>> &es) {
    shuffle(ALL(es));
    vector<int> ord(n);
    iota(ALL(ord), 0);
    shuffle(ALL(ord));
    for (auto &[u, v] : es)
        u = ord[u], v = ord[v];
}
template <bool directed, bool simple> vector<pair<int, int>> genGraph(int n) {
    vector<pair<int, int>> cand, es;
    rep(u, 0, n) rep(v, 0, n) {
        if (simple and u == v)
            continue;
        if (!directed and u > v)
            continue;
        cand.push_back({u, v});
    }
    int m = get(SZ(cand));
    vector<int> ord;
    if (simple)
        ord = select(m, 0, SZ(cand) - 1);
    else {
        rep(_, 0, m) ord.push_back(get(SZ(cand) - 1));
    }
    for (auto &i : ord)
        es.push_back(cand[i]);
    relabel(n, es);
    return es;
}
vector<pair<int, int>> genTree(int n) {
    vector<pair<int, int>> es;
    rep(i, 1, n) es.push_back({get(i - 1), i});
    relabel(n, es);
    return es;
}
}; // namespace Random


/**
 * @brief Random
 */

vector<int> BiMatching(int n, int m, vector<vector<int>> &g) {
    rep(v, 0, n) Random::shuffle(ALL(g[v]));
    vector<int> L(n, -1), R(m, -1);
    for (;;) {
        vector<int> par(n, -1), root(n, -1);
        queue<int> que;
        rep(i, 0, n) if (L[i] == -1) {
            que.push(i);
            root[i] = i;
        }
        bool upd = 0;
        while (!que.empty()) {
            int v = que.front();
            que.pop();
            if (L[root[v]] != -1)
                continue;
            for (auto u : g[v]) {
                if (R[u] == -1) {
                    while (u != -1) {
                        R[u] = v;
                        swap(L[v], u);
                        v = par[v];
                    }
                    upd = 1;
                    break;
                }
                int to = R[u];
                if (par[to] == -1) {
                    root[to] = root[v];
                    par[to] = v;
                    que.push(to);
                }
            }
        }
        if (!upd)
            break;
    }
    return L;
}

/**
 * @brief Bipartite Matching
 */

int main() {
while(1) {
    int N, M;
    cin >> N >> M;
    if (N == 0 && M == 0) return 0;
    vector<int> A(N), B(M);
    rep(i,0,N) cin >> A[i];
    rep(i,0,M) cin >> B[i];
    vector<vector<int>> G(N);
    rep(i,0,N) {
        rep(j,0,M) {
            if (gcd(A[i],B[j]) >= 2) G[i].push_back(j);
        }
    }
    vector<int> V = BiMatching(N,M,G);
    int ANS = 0;
    rep(i,0,N) if (V[i] != -1) ANS++;
    cout << ANS << endl;
}
}
accuracy: 1
time_ms: 600
memory_kb: 3888
final_rank: 1