// 5/19
// ANT2-4-3. Union Find Tree, 食物連鎖
// 類題
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using P = pair<int, int>;
using LLP = pair<ll, ll>;
using Graph = vector<vector<int>>;

const int MAX_N = 200010;
int par[MAX_N];  // 親ノードの番号を格納。i = par[i]なiはルート
int tree_rank[MAX_N];  // 深さ。

/* 以下、Union Find Treeの実装 */

// n要素で初期化
void init(int n) {
    rep(i, n) {
        par[i] = i;
        tree_rank[i] = 0;
    }
}

// 木の根を求める
int find(int x) {
    if (par[x] == x) {
        return x;
    } else {
        return par[x] = find(par[x]);  // ルートの更新を自動でやっている。
    }
}

// xとyの属する集合を併合
void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return;  // 既に同じ集合に属する

    if (tree_rank[x] < tree_rank[y]) {  // yの方が深い場合、xのルートはyのルートの直下につなげる。
        par[x] = y;  // 本当に賢い。ルートの親を変えることで付け替えられる。
    } else {
        par[y] = x;
        if (tree_rank[x] == tree_rank[y]) tree_rank[x]++;
    }
}

// xとyが同じ集合に属するかいなか
bool same(int x, int y) {
    return find(x) == find(y);
}

// int par2[MAX_N];  // 親ノードの番号を格納。i = par[i]なiはルート
// int tree_rank2[MAX_N];  // 深さ。

// /* 以下、Union Find Treeの実装 */

// // n要素で初期化
// void init2(int n) {
//     rep(i, n) {
//         par2[i] = i;
//         tree_rank2[i] = 0;
//     }
// }

// // 木の根を求める
// int find2(int x) {
//     if (par2[x] == x) {
//         return x;
//     } else {
//         return par2[x] = find2(par2[x]);  // ルートの更新を自動でやっている。
//     }
// }

// // xとyの属する集合を併合
// void unite2(int x, int y) {
//     x = find2(x);
//     y = find2(y);
//     if (x == y) return;  // 既に同じ集合に属する

//     if (tree_rank2[x] < tree_rank2[y]) {  // yの方が深い場合、xのルートはyのルートの直下につなげる。
//         par2[x] = y;  // 本当に賢い。ルートの親を変えることで付け替えられる。
//     } else {
//         par2[y] = x;
//         if (tree_rank2[x] == tree_rank2[y]) tree_rank2[x]++;
//     }
// }

// // xとyが同じ集合に属するかいなか
// bool same2(int x, int y) {
//     return find2(x) == find2(y);
// }

struct UnionFind {
  vector<int> d;
  UnionFind(int n=0): d(n,-1) {}
  int find(int x) {
    if (d[x] < 0) return x;
    return d[x] = find(d[x]);
  }
  bool unite(int x, int y) {
    x = find(x); y = find(y);
    if (x == y) return false;
    if (d[x] > d[y]) swap(x,y);
    d[x] += d[y];
    d[y] = x;
    return true;
  }
  bool same(int x, int y) { return find(x) == find(y);}
  int size(int x) { return -d[find(x)];}
};

int main() {
    int N, K, L;
    cin >> N >> K >> L;

    init(N);
    // init2(N);

    // vector<int> R(L), S(L);
    UnionFind roads(N), rails(N);

    rep(i, K) {
        int p, q;
        cin >> p >> q;
        p--;
        q--;
        // unite(p, q);
        roads.unite(p, q);
    }
    // rep(i, L) {
    //     int r, s;
    //     cin >> r >> s;
    //     // r--; s--;
    //     R[i] = r - 1;
    //     S[i] = s - 1;
    //     // unite2(r, s);
    // }

    // rep(i, )

    // int ans = 0;
    // vector<int> ans(N, 1);

    // rep(i, L) {
    //     if (same(R[i], S[i])) ans[R[i]]++; ans[S[i]]++;
    // }

    // rep(i, N) {
    //     cout << ans[i];
    //     if (i != N - 1) cout << " ";
    // }
    // cout << endl;

    rep(i, L) {
        int r, s;
        cin >> r >> s;
        r--; s--;
        // int r = R[i];
        // int s = S[i];

        rails.unite(r, s);

        // // printf("same(%d, %d) = %d\n", r, s, same(r, s));

        // if (same(r, s)) {
        //     ans[r]++;
        //     ans[s]++;
        // }
        // rep(i, N) {
        //     cout << ans[i];
        //     if (i != N - 1) cout << " ";
        // }
        // cout << endl;
    }

    map<P, int> ans;

    // rep(i, N) {
    //     cout << ans[i];
    //     if (i != N - 1) cout << " ";
    // }
    // cout << endl;

    rep(i, N) {
        ans[make_pair(roads.find(i), rails.find(i))]++;
    }
    rep(i, N) {
        cout << ans[make_pair(roads.find(i), rails.find(i))];
        if (i != N - 1) cout << " ";
    }
    cout << endl;
}


Measured memory_bytes: 15104