#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <algorithm>
#include <set>
#include <map>
#include <bitset>
#include <cmath>
#include <functional>
#include <iomanip>
#define vll vector<ll>
#define vvvl vector<vvl>
#define vvl vector<vector<ll>>
#define VV(a, b, c, d) vector<vector<d>>(a, vector<d>(b, c))
#define VVV(a, b, c, d) vector<vvl>(a, vvl(b, vll (c, d)));
#define re(c, b) for(ll c=0;c<b;c++)
#define all(obj) (obj).begin(), (obj).end()
typedef long long int ll;
typedef long double ld;
using namespace std;


#define P 998244353
#define N_MAX 10000000
typedef long long int ll;
ll fac[N_MAX+1];
ll inv[N_MAX+1];
ll finv[N_MAX+1];

ll comb(ll n, ll k){
  if(n<0||k<0||n<k) return 0;
  return (((fac[n]*finv[n-k])%P)*finv[k])%P;
}
ll perm(ll n, ll k){
  if(n<0||k<0||n<k) return 0;
  return (fac[n]*finv[n-k])%P;
}

void init(){
  fac[0] = finv[0] = fac[1] = finv[1] = inv[1] = 1;
  for(int i = 2; i <= N_MAX; i++){
    fac[i] = (fac[i-1]*i)%P;
    inv[i] = ((-(P/i)*inv[P%i])%P+P)%P;
    finv[i] = (finv[i-1]*inv[i])%P;
  }
}
ll pp(ll a, ll b){
  return (a * b)%P;
}

typedef vector<vector<ll>> graph;
ll n;

struct Merge{
  set<vll> lr;
  ll INF = 1000000000000000009;
  vll remove(ll a, ll b){
    if(a>b) swap(a, b);
    auto itr = lr.lower_bound(vll{b+1, 0});
    if(itr==lr.begin()) return vll{a, b};
    itr--;
    ll lef = a, ri = b;
    while((*itr)[1]>=a){
      ll now_x = (*itr)[0], now_y = (*itr)[1];
      lef = min(lef, now_x), ri = max(ri, now_y);
      if(itr==lr.begin()){
        lr.erase(itr);
        break;
      }
      itr--;
      lr.erase(vll{now_x, now_y});
    }
    return vll{lef, ri};
  }
  void merge(ll a, ll b){lr.insert(remove(a, b));}

  vll get(ll a){
    auto itr = lr.lower_bound(vll{a+1, a+1});
    if(lr.size()==0||itr==lr.begin()) return vll{INF, INF};
    itr--;
    if((*itr)[1]<a) return vll{INF, INF};
    return *itr;
  }
  bool same(ll a, ll b){
    if(get(a)[0]==-1) return false;
    return get(a)[0]==get(b)[0];
  }
};

vvl get(graph g){
  //なるべく大きい要素を取る独立集合
  //現在とったものに隣接していない最大の要素を貪欲に取っていく
  vvl dat;
  vll go(n+1, 10000000);

  for(int i=n;i>0;i--){
    Merge mg;
    for(auto to:g[i]){
      if(to<i) continue;
      mg.merge(go[to], go[to]+1);
    }
    auto itr = mg.lr.begin();
    if(mg.lr.size()==0||(*itr)[0]!=0) go[i] = 0;
    else go[i] = (*itr)[1];

    if(dat.size()<=go[i]) dat.push_back(vll());
    dat[go[i]].push_back(i);
  }
  return dat;
}

vector<ll> cost(400000, 1);


//独立集合の重み
ll get_cost(vll v){
  ll ret = 0;
  for(int i=0;i<v.size();i++) ret = (ret + cost[v[i]])%P;
  return ret;
}

int main(int argc, char const *argv[]) {
  init();
  ll x = 1000000000000000000LL%P;
  for(int i=1;i<400000;i++) cost[i] = (cost[i-1] * x)%P;
  std::cin >> n;
  ll lm = 10000;
  vvl gr = VV(3, lm, 0, ll);

  for(int i=0;i<3;i++){
    ll m;std::cin >> m;
    graph g(n+1);
    for(int j=0;j<m;j++){
      ll a, b;scanf("%lld %lld", &a, &b);
      g[a].push_back(b);
      g[b].push_back(a);
    }
    vvl X = get(g);
    for(int j=0;j<min((ll)X.size(), lm);j++){
      for(auto v:X[j]) gr[i][j] = (gr[i][j] + cost[v])%P;
    }
  }

  ll ans = 0;
  for(int i=0;i<lm;i++){
    for(int j=0;j<lm;j++){
      ll xo = i^j;
      if(xo >= lm) continue;
      ans = (ans + pp(gr[0][i], pp(gr[1][j], gr[2][xo])))%P;
    }
  }
  std::cout << ans << '\n';
  return 0;
}


Measured memory_bytes: 253316