#include <bits/stdc++.h>
using ll = long long;
template <class t, class u, class... w>
auto operator<<(t& o, u a) -> decltype(a.print(a), o) {
  return o << "{", a.print(), o << "}";
}
using namespace std;
void DD(...) {}
using Pii = pair<int, int>;
template <class t, class u, class... w>
void DD(t s, u a, w... k) {
  while (*s && *s != ',') cerr << *s++;
  cerr << ": " << a << *s++;
  DD(s, k...);
}
template <class t, class u, class... w>
auto operator<<(t& o, u a) -> decltype((a).begin(), (a).end(), o) {
  return o << "[",
         [&]() {
           for (auto i : a) o << i << ", ";
         }(),
         o << "]";
}
using Vi = vector<int>;
template <class t, class u, class... w>
auto operator<<(t& o, u a) -> decltype(get<1>(a), o) {
  return o << "(" << a.first << ", " << a.second << ")";
}
constexpr ll MOD = 998244353;
ll modInv(ll a, ll m) {
  if (a == 1) return 1;
  return ((a - modInv(m % a, a)) * m + 1) / a;
}
ll umod(ll a, ll m) {
  a %= m;
  return (a >= 0 ? a : a + m);
}
struct Zp {
  ll first;
  Zp(ll second = 0) {
    first = (second < 0 || second >= MOD ? umod(second, MOD) : second);
  }
  Zp operator-() const { return MOD - first; }
  Zp operator+(Zp r) const { return first + r.first; }
  Zp operator-(Zp r) const { return first - r.first; }
  Zp operator*(Zp r) const { return first * r.first; }
  Zp operator/(Zp r) const { return first * r.inv().first; }
  Zp& operator+=(Zp r) {
    *this = *this + r;
    return *this;
  }
  Zp inv() const { return modInv(first, MOD); }
  Zp pow(ll e) const {
    if (!e) return 1;
    Zp t = pow(e / 2);
    return t * t * (e & 1 ? first : 1);
  }
  void print(...) { cerr << first; }
};
struct Vert {
  Vi edges;
  unordered_map<int, Zp> dyn[40];
};
int n1, n2, k;
vector<Vert> V1, V2;
void loadTree(vector<Vert>& V, int n) {
  V.resize(n);
  for (int i = (1); i < (n); i++) {
    int a, b;
    cin >> a >> b;
    a--;
    b--;
    V[a].edges.push_back(b);
    V[b].edges.push_back(a);
  }
}
void compute(vector<Vert>& V) {
  for (auto& vert : (V)) {
    for (auto& e : (vert.edges)) vert.dyn[0][e] = 1;
    vert.dyn[0][-1] = 1;
  }
  for (int len = (1); len < (k + 1); len++)
    for (int id = (0); id < (int((V).size())); id++) {
      auto& vert = V[id];
      for (int part = (0); part < (len); part++) {
        Zp tail = 0;
        for (auto& e : (vert.edges)) tail += V[e].dyn[len - part - 1][id];
        vert.dyn[len][-1] += vert.dyn[part][-1] * tail;
        for (auto& e : (vert.edges))
          vert.dyn[len][e] +=
              vert.dyn[part][e] * (tail - V[e].dyn[len - part - 1][id]);
      }
    }
}
int main() {
  cin.sync_with_stdio(0);
  cin.tie(0);
  cout << fixed << setprecision(10);
  cin >> n1 >> n2 >> k;
  loadTree(V1, n1);
  loadTree(V2, n2);
  if (k % 2) {
    cout << "0\n";
    return 0;
  }
  k /= 2;
  compute(V1);
  compute(V2);
  Zp newton = 1;
  Zp ret = 0;
  for (int m = (0); m < (k + 1); m++) {
    Zp t1 = 0, t2 = 0;
    for (auto& vert : (V1)) t1 += vert.dyn[m][-1];
    for (auto& vert : (V2)) t2 += vert.dyn[k - m][-1];
    ret += newton * t1 * t2;
    newton = newton / ((2 * m + 1) * (2 * m + 2)) * (2 * k - 2 * m) *
             (2 * k - 2 * m - 1);
  }
  cout << ret.first << endl;
  return 0;
}
