unittest
{
	assert( [ "4 3 1", "1 2", "2 3", "3 4", "2 3" ].parse.expand.solve == "1 2 2 1" );
	assert( [ "4 2 2", "1 2", "2 3", "1 4", "2 3" ].parse.expand.solve == "1 2 2 1" );
	assert( [ "7 4 4", "1 2", "2 3", "2 5", "6 7", "3 5", "4 5", "3 4", "6 7" ].parse.expand.solve == "1 1 2 1 2 2 2" );
}



import std.algorithm;
import std.conv;
import std.range;
import std.stdio;
import std.typecons;

void main()
{
	stdin.byLineCopy.parse.expand.solve.writeln;
}

auto parse( Range )( Range input )
if( isInputRange!Range && is( ElementType!Range == string ) )
{
	auto nkl = input.front.split.to!( long[] );
	auto pqs = new long[ 2 ][ nkl[ 1 ] ];
	foreach( ref pq; pqs )
	{
		input.popFront;
		pq = input.front.split.to!( long[] );
		pq[] -= 1;
	}
	auto rss = new long[ 2 ][ nkl[ 2 ] ];
	foreach( ref rs; rss )
	{
		input.popFront;
		rs = input.front.split.to!( long[] );
		rs[] -= 1;
	}
	return tuple( nkl[ 0 ], pqs, rss );
}

auto solve( long n, long[ 2 ][] pqs, long[ 2 ][] rss )
{
	// 道路連結グループを判定
	auto uft_a = UnionFindTree( n );
	foreach( pq; pqs )
	{
		uft_a.unite( pq[ 0 ], pq[ 1 ] );
	}
	auto gss_a = new bool[ long ][ n ];
	foreach( i; 0 .. n )
	{
		gss_a[ uft_a.root( i ) ][ i ] = true;
	}
	
	// 鉄道連結グループを判定
	auto uft_b = UnionFindTree( n );
	foreach( rs; rss )
	{
		uft_b.unite( rs[ 0 ], rs[ 1 ] );
	}
	auto gss_b = new bool[ long ][ n ];
	foreach( i; 0 .. n )
	{
		gss_b[ uft_b.root( i ) ][ i ] = true;
	}
	
	// 道路連結グループと鉄道連結グループのANDを取る
	auto anss = new long[ n ];
	foreach( i; 0L .. n )
	{
		if( 0 < anss[ i ] ) continue;
		
		auto gs_a = gss_a[ uft_a.root( i ) ];
		auto gs_b = gss_b[ uft_b.root( i ) ];
		if( gs_b.length < gs_a.length )
		{
			auto gs_c = gs_a;
			gs_a = gs_b;
			gs_b = gs_c;
		}
		
		bool[ long ] gs;
		foreach( k, g; gs_a )
		{
			if( k !in gs_b ) continue;
			gs[ k ] = true;
			gs_a.remove( k );
			gs_b.remove( k );
		}
		foreach( k, g; gs )
		{
			anss[ k ] = gs.length;
		}
	}
	return anss.to!( string[] ).join( " " );
}

// UnionFind木
struct UnionFindTree
{
	private long[] ps;
	private long[] rs;
	
	// 重み無し初期化
	public this( long n )
	{
		ps.length = n;
		ps[] = -1;		// マイナスは根であり、その木に属するノードの数でもある。
		rs.length = n;
	}
	
	// 重み付き初期化（重みは必ず1以上にすること。0もダメ）
	public this( long[] ws )
	{
		ps.length = ws.length;
		rs.length = ws.length;
		foreach( ref p, w; lockstep( ps, ws ) ) p = -w;		// マイナスは根であり、その木の重みの合計でもある。
	}
	
	public long root( long i )
	{
		if( ps[ i ] < 0 ) return i;
		else              return ps[ i ] = root( ps[ i ] );
	}
	
	public bool same( long i, long j )
	{
		return root( i ) == root( j );
	}
	
	public void unite( long i, long j )
	{
		i = root( i );
		j = root( j );
		if( i == j ) return;
		if( rs[ i ] < rs[ j ] )
		{
			ps[ j ] += ps[ i ];
			ps[ i ] = j;
		}
		else
		{
			ps[ i ] += ps[ j ];
			ps[ j ] = i;
			if( rs[ i ] == rs[ j ] ) rs[ i ]++;
		}
	}
}
