import std.algorithm, std.container, std.conv, std.math, std.range, std.typecons, std.stdio, std.string;

auto rdsp(){return readln.splitter;}
void pick(R,T)(ref R r,ref T t){t=r.front.to!T;r.popFront;}
void readV(T...)(ref T t){auto r=rdsp;foreach(ref v;t)pick(r,v);}
void writeA(T)(size_t n,T t){foreach(i,v;t.enumerate){write(v);if(i<n-1)write(" ");}writeln;}

void main()
{
  int n, k, l; readV(n, k, l);

  auto ufk = UnionFind!int(n);
  foreach (_; 0..k) {
    int p, q; readV(p, q); --p; --q;
    ufk.unite(p, q);
  }

  auto ufl = UnionFind!int(n);
  foreach (_; 0..l) {
    int r, s; readV(r, s); --r; --s;
    ufl.unite(r, s);
  }

  struct F { int k, l; }
  auto f = new F[](n);
  foreach (i; 0..n) f[i] = F(ufk[i], ufl[i]);

  auto fs = f.dup.sort!"a.k == b.k ? a.l < b.l : a.k < b.k";
  auto r = new int[](n);
  foreach (i; 0..n) r[i] = fs.equalRange(f[i]).length.to!int;

  writeA(n, r);
}

struct UnionFind(T)
{
  import std.algorithm, std.range;

  T[] p; // parent
  const T s; // sentinel
  const T n;
  T countForests; // number of forests
  T[] countNodes; // number of nodes in forests

  this(T n)
  {
    this.n = n;
    s = n;
    p = new T[](n);
    p[] = s;
    countForests = n;
    countNodes = new T[](n);
    countNodes[] = 1;
  }

  T opIndex(T i)
  {
    if (p[i] == s) {
      return i;
    } else {
      p[i] = this[p[i]];
      return p[i];
    }
  }

  bool unite(T i, T j)
  {
    auto pi = this[i], pj = this[j];
    if (pi != pj) {
      p[pj] = pi;
      --countForests;
      countNodes[pi] += countNodes[pj];
      return true;
    } else {
      return false;
    }
  }

  auto countNodesOf(T i) { return countNodes[this[i]]; }
  bool isSame(T i, T j) { return this[i] == this[j]; }

  auto groups()
  {
    auto g = new T[][](n);
    foreach (i; 0..n) g[this[i]] ~= i;
    return g.filter!(l => !l.empty);
  }
}
