#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>
#include <set>
#include <map>
#include <vector>
#include <list>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <queue>
#include <bitset>     //UWAGA - w czasie kompilacji musi byc znany rozmiar wektora - nie mozna go zmienic
#include <cassert>
#include <iomanip>        //do setprecision
#include <ctime>
#include <complex>
using namespace std;

#define FOR(i,b,e) for(int i=(b);i<(e);++i)
#define FORQ(i,b,e) for(int i=(b);i<=(e);++i)
#define FORD(i,b,e) for(int i=(b)-1;i>=(e);--i)
#define REP(x, n) for(int x = 0; x < (n); ++x)

#define ST first
#define ND second
#define PB push_back
#define MP make_pair
#define LL long long
#define ULL unsigned LL
#define LD long double

const double pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342;
const int mod=1000000007;
vector<int> i2g;          // i2g[i] := アイテム i の所属するグループの番号(find)
vector<vector<int>> g2i;  // g2i[g] := グループ g に所属するアイテムたち

void union_init(int n) {
  i2g.resize(n);
  g2i.resize(n);
  for (int i = 0; i < n; ++i) {
    // 最初はアイテム i はグループ i に所属
    i2g[i] = i;
    g2i[i].assign(1, i);
  }
}

int find(int i){
  return i2g[i];
}

// アイテム ia の所属するグループとアイテム ib の所属するグループを 1 つにする
void merge(int ia, int ib) {
  if(i2g[ia]==i2g[ib])return;
  // ia の所属するグループが ib の所属するグループより小さくならないようにする
  if (g2i[i2g[ia]].size() < g2i[i2g[ib]].size()) {
    swap(ia, ib);
  }
  int ga = i2g[ia], gb = i2g[ib];
  // グループ gb に所属する全てのアイテムをグループ ga に移す
  for (int j : g2i[gb]) i2g[j] = ga;
  g2i[ga].insert(g2i[ga].end(), g2i[gb].begin(), g2i[gb].end());
  g2i[gb].clear();
}

// アイテム ia とアイテム ib は同じグループに所属しているか？
bool is_same_group(int ia, int ib) {
  return i2g[ia] == i2g[ib];
}

typedef pair<int,pair<int,int>> edge; //<cost,<from,to>>
priority_queue<edge,vector<edge>,greater<edge>> eque;

LL kruskal(int v){//頂点数(番号のmax) 使用前にequeにedge詰めておく
  LL cost=0;
  union_init(v);
  while(!eque.empty()){
    edge ed=eque.top();
    eque.pop();
    if(!is_same_group(ed.ND.ST,ed.ND.ND)){
      merge(ed.ND.ST,ed.ND.ND);
      cost+=ed.ST;
			//cout<<"merge... cost:"<<ed.ST<<" /"<<ed.ND.ST<<"&"<<ed.ND.ND<<endl;
    }
}
  return cost;
}
//////////////////////////////////////////////////////////////////////////
vector<int> i2gb;          // i2g[i] := アイテム i の所属するグループの番号(find)
vector<vector<int>> g2ib;  // g2i[g] := グループ g に所属するアイテムたち

void union_initb(int n) {
  i2gb.resize(n);
  g2ib.resize(n);
  for (int i = 0; i < n; ++i) {
    // 最初はアイテム i はグループ i に所属
    i2gb[i] = i;
    g2ib[i].assign(1, i);
  }
}

int findb(int i){
  return i2gb[i];
}

// アイテム ia の所属するグループとアイテム ib の所属するグループを 1 つにする
void mergeb(int ia, int ib) {
  if(i2gb[ia]==i2gb[ib])return;
  // ia の所属するグループが ib の所属するグループより小さくならないようにする
  if (g2ib[i2gb[ia]].size() < g2ib[i2gb[ib]].size()) {
    swap(ia, ib);
  }
  int ga = i2gb[ia], gb = i2gb[ib];
  // グループ gb に所属する全てのアイテムをグループ ga に移す
  for (int j : g2ib[gb]) i2gb[j] = ga;
  g2ib[ga].insert(g2ib[ga].end(), g2ib[gb].begin(), g2ib[gb].end());
  g2ib[gb].clear();
}

// アイテム ia とアイテム ib は同じグループに所属しているか？
bool is_same_groupb(int ia, int ib) {
  return i2gb[ia] == i2gb[ib];
}


///////////////////////////////////////////////////////////////////////////
int main(){
	int n,k,l;
	cin>>n>>k>>l;
	union_init(n);
	union_initb(n);
	int p,q;
	FOR(i,0,k){
		cin>>p>>q;
		merge(p-1,q-1);
	}
	FOR(i,0,l){
		cin>>p>>q;
		mergeb(p-1,q-1);
	}
	map<pair<int,int>,int> mp;
	FOR(i,0,n){
		mp[MP(i2g[i],i2gb[i])]++;
	}
	FOR(i,0,n){
		cout<<mp[MP(i2g[i],i2gb[i])]<<" ";
	}
	cout<<endl;
    return 0;
}


Measured memory_bytes: 34984