#include <bits/stdc++.h>
using namespace std;
#define sim template < class c
#define ris return * this
#define dor > debug & operator <<
#define eni(x) sim > typename \
  enable_if<sizeof dud<c>(0) x 1, debug&>::type operator<<(c i) {
sim > struct rge { c b, e; };
sim > rge<c> range(c i, c j) { return rge<c>{i, j}; }
sim > auto dud(c* x) -> decltype(cerr << *x, 0);
sim > char dud(...);
struct debug {
#ifdef LOCAL
~debug() { cerr << endl; }
eni(!=) cerr << boolalpha << i; ris; }
eni(==) ris << range(begin(i), end(i)); }
sim, class b dor(pair < b, c > d) {
  ris << "(" << d.first << ", " << d.second << ")";
}
sim dor(rge<c> d) {
  *this << "[";
  for (auto it = d.b; it != d.e; ++it)
	*this << ", " + 2 * (it == d.b) << *it;
  ris << "]";
}
#else
sim dor(const c&) { ris; }
#endif
};
#define imie(...) " [" << #__VA_ARGS__ ": " << (__VA_ARGS__) << "] "

const int MOD = 998244353;
vector<int> powers;

int n;
struct Graph {
	vector<vector<int>> edges;
	vector<int> nimber;
	vector<int> groups;
	// vector<int> good, worse;
	// vector<bool> forbidden;
	// void forbid_nei(int a) {
		// for(int b : edges[a]) {
			// forbidden[b] = true;
		// }
		// edges[a].clear();
	// }
	void read() {
		edges.resize(n + 1);
		nimber.resize(n + 1);
		// forbidden.resize(n + 1);
		int m;
		cin >> m;
		for(int i = 0; i < m; ++i) {
			int a, b;
			cin >> a >> b;
			edges[a].push_back(b);
			edges[b].push_back(a);
		}
		for(int a = n; a >= 1; --a) {
			set<int> nei;
			for(int b : edges[a]) {
				if(b > a) {
					nei.insert(nimber[b]);
				}
			}
			for(int value = 0; true; ++value) {
				if(!nei.count(value)) {
					nimber[a] = value;
					break;
				}
			}
			int i = nimber[a];
			while((int) groups.size() <= i) {
				groups.push_back(0);
			}
			groups[i] += powers[a];
			groups[i] %= MOD;
		}
		// debug() << imie(good) imie(worse);
	}
};

int main() {
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	
	cin >> n;
	powers.resize(n + 1);
	powers[0] = 1;
	for(int i = 1; i <= n; ++i) {
		powers[i] = (long long) 1'000'000'000'000'000'000 % MOD * powers[i-1] % MOD;
	}
	
	Graph graphs[3];
	for(int i : {0, 1, 2}) {
		graphs[i].read();
	}
	int hope = 0;
	// int hope = graphs[0].good.size() * graphs[1].good.size() * graphs[2].good.size();
	// for(int i : {0, 1, 2}) {
		// hope += graphs[i].good.size() * graphs[(i+1)%3].worse.size() * graphs[(i+2)%3].worse.size();
	// }
	set<vector<int>> forbidden;
	int count = 0;
	for(int a = 0; a < (int) graphs[0].groups.size(); ++a) {
		for(int b = 0; b < (int) graphs[1].groups.size(); ++b) {
			int c = a ^ b;
			if(c < (int) graphs[2].groups.size()) {
			// for(int c = 0; c < (int) graphs[2].groups.size(); ++c) {
				if((a ^ b ^ c) == 0) {
					hope += (long long) graphs[0].groups[a] * graphs[1].groups[b] % MOD * graphs[2].groups[c] % MOD;
					hope %= MOD;
				}
			}
		}
	}
	printf("%d\n", hope);
	return 0;
	
	
	
	
	
	
	for(int s = 3 * n; s >= 0; --s) {
		for(int a = 1; a <= n; ++a) {
			for(int b = 1; b <= n; ++b) {
				for(int c = 1; c <= n; ++c) {
					if(a + b + c == s) {
						vector<int> triple{a, b, c};
						if(!forbidden.count(triple)) {
							if(a <= b && b <= c) { 
								for(int x : triple) {
									cerr << n-x;
								}
								cerr << endl;
							}
							int my_xor = 0;
							for(int i : {0, 1, 2}) {
								my_xor ^= graphs[i].nimber[triple[i]];
							}
							if(my_xor == 0) {
								hope++;
							}
							++count;
							// debug() << imie(triple);
							for(int a2 : graphs[0].edges[a]) {
								forbidden.insert({a2, b, c});
							}
							for(int b2 : graphs[1].edges[b]) {
								forbidden.insert({a, b2, c});
							}
							for(int c2 : graphs[2].edges[c]) {
								forbidden.insert({a, b, c2});
							}
						}
						// bool ok = true;
						// for(int i : {0, 1, 2}) {
							// if(graphs[i].forbidden[triple[i]]) {
								// ok = false;
							// }
						// }
						// if(!ok) {
							// continue;
						// }
						// debug() << imie(a) imie(b) imie(c);
						// for(int i : {0, 1, 2}) {
							// graphs[i].forbid_nei(triple[i]);
						// }
					}
				}
			}
		}
	}
	debug() << imie(count);
	debug() << imie(hope);
	assert(count == hope);
}

// 2 -- 5 in the first graph

// (2, 8, 10) -- (5, 8, 10)
// (2, 8, 10) !- (2, 4, 8)


// N = 5


// (2, 5, 5)
// (5, 5, 5)


// (3, 8, 6) <- when should we take this?  there is edge 3--7
// (7, 8, 6) <- not taken
// (3, 10, 6) <- not taken
// (7, 10, 6) <- taken


// (7, 8, 9) - TAKE
// (9, 8, 9) - NO
// (4, 9, 9) - NO
// (7, 9, 9) - NO
// (9, 9, 9) - TAKE


// (8, 8, 8) - NO
// (9, 8, 8) - TAKE
// (8, 9, 8) - TAKE
// (8, 8, 9) - TAKE
// (9, 9, 8) - NO
// (9, 8, 9) - NO
// (8, 9, 9) - NO
// (9, 9, 9) - TAKE


// 888 - TAKE ? 
// 898 - NO
// 988 - NO
// 998 - TAKE
// 999 - TAKE



// 998 - TAKE
// 999 - TAKE

// in a graph, I have merged group 987, then there is 6

// 9875
// 6      5--6



// ab9   NOT
// ab5   if 5--9


// 987
// 6
// 5


// 36 + 6 + 8 + 3 = 53


Measured memory_bytes: 17280