30 30 10
6 1
8 27
5 1
21 6
24 15
12 5
29 7
22 7
23 17
2 13
23 11
29 6
14 18
29 16
27 25
15 2
27 4
19 30
28 19
17 2
27 16
3 4
9 25
19 16
10 16
18 17
26 11
2 27
16 20
13 23
1 4
27 7
6 20
17 30
1 18
2 17
1 11
5 9
25 13
11 20
14 25
30 21
6 24
12 2
25 12
22 13
10 27
24 29
19 24
28 29
24 23
9 6
12 15
11 15
23 14
12 28
28 25
2 11
19 30
 2 2 1
1 2
2 1
1 2

0 1
#include <bits/stdc++.h>
using namespace std;
const pair<long long, long long> EPT(0, 0);
pair<long long, long long> operator+(const pair<long long, long long> &A,
                                     const pair<long long, long long> &B) {
  return pair<long long, long long>(A.first + B.first, A.second + B.second);
}
vector<int> from[150010], G[150010];
int fa[150010][18], id[150010], TOT, sz[150010], dep[150010], QRY;
int mul[1000010], val[1000000];
vector<pair<int, int> > qry[150010];
vector<int> a[150010];
vector<int> has[150010];
vector<pair<pair<int, int>, pair<int, int> > > all[150010];
vector<pair<int, int> > rub;
vector<int> node;
int n;
long long ans;
int T[150010];
int st[150010];
void modify(int x, int v) {
  while (x <= n) T[x] += v, x += x & -x;
}
int query(int x) {
  int res = 0;
  while (x) res += T[x], x -= x & -x;
  return res;
}
long long F(int x) { return 1ll * x * (x - 1) / 2; }
void dfs(int x, int last) {
  id[x] = ++TOT, sz[x] = 1, fa[x][0] = last;
  for (int i = 0; i < from[x].size(); i++) {
    int v = from[x][i];
    if (v == last) continue;
    dep[v] = dep[x] + 1, dfs(v, x), sz[x] += sz[v];
  }
}
void get_fa() {
  for (int j = 1; j < 18; j++) {
    for (int i = 1; i <= n; i++) {
      fa[i][j] = fa[fa[i][j - 1]][j - 1];
    }
  }
}
int get_lca(int x, int y) {
  while (x != y) {
    if (dep[x] < dep[y]) swap(x, y);
    x = fa[x][0];
  }
  return x;
  if (dep[x] < dep[y]) swap(x, y);
  int tmp = dep[x] - dep[y], cnt = 0;
  while (tmp) {
    if (tmp & 1) x = fa[x][cnt];
    tmp >>= 1, cnt++;
  }
  if (x == y) return x;
  for (int i = 17; i >= 0; i--) {
    if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
  }
  return fa[x][0];
}
int get_par(int x, int tmp) {
  int cnt = 0;
  while (tmp) {
    if (tmp & 1) x = fa[x][cnt];
    tmp >>= 1, cnt++;
  }
  return x;
}
bool by_id(int x, int y) { return id[x] < id[y]; }
bool is_anc(int x, int y) { return id[x] < id[y] && id[y] < id[x] + sz[x]; }
int BuildTree() {
  sort(node.begin(), node.end(), by_id),
      node.resize(unique(node.begin(), node.end()) - node.begin());
  for (int i = (int)node.size() - 1; i >= 1; i--) {
    node.push_back(get_lca(node[i], node[i - 1]));
  }
  sort(node.begin(), node.end(), by_id),
      node.resize(unique(node.begin(), node.end()) - node.begin());
  for (int i = 0; i < node.size(); i++) {
    G[node[i]].clear(), has[node[i]].clear();
  }
  int tp = 0;
  for (int i = 0; i < node.size(); i++) {
    int x = node[i];
    while (tp && !is_anc(st[tp - 1], x)) tp--;
    if (tp) G[st[tp - 1]].push_back(x);
    st[tp++] = x;
  }
  return node[0];
}
struct Node {
  pair<long long, long long> sum;
  Node *lson, *rson;
  void pushup() { sum = (lson ? lson->sum : EPT) + (rson ? rson->sum : EPT); }
} pool[3000010], *CUR;
void Update(Node *&T, int l, int r, int pos, pair<long long, long long> x) {
  if (!T) T = CUR++, T->sum = EPT, T->lson = T->rson = NULL;
  if (l == r) {
    T->sum = T->sum + x;
    return;
  }
  int mid = l + r >> 1;
  if (pos <= mid)
    Update(T->lson, l, mid, pos, x);
  else
    Update(T->rson, mid + 1, r, pos, x);
  T->pushup();
}
pair<long long, long long> Query(Node *T, int l, int r, int L, int R) {
  if (!T) return EPT;
  if (l == L && r == R) return T->sum;
  int mid = l + r >> 1;
  if (R <= mid) return Query(T->lson, l, mid, L, R);
  if (L > mid) return Query(T->rson, mid + 1, r, L, R);
  return Query(T->lson, l, mid, L, mid) +
         Query(T->rson, mid + 1, r, mid + 1, R);
}
Node *Merge(Node *L, Node *R) {
  if (!L || !R) return L ? L : R;
  L->sum = L->sum + R->sum;
  L->lson = Merge(L->lson, R->lson);
  L->rson = Merge(L->rson, R->rson);
  return L;
}
struct Block {
  Node *T;
  vector<int> *a;
  Block() { T = NULL, a = NULL; }
  Block(int x) {
    T = NULL, Update(T, 1, n, id[x], pair<long long, long long>(1, dep[x])),
    a = new vector<int>, a->push_back(x);
  }
};
Block Merge(Block A, Block B, int x, int lca, int K, int flag) {
  if (A.a == NULL || B.a == NULL) return A.a == NULL ? B : A;
  if (A.a->size() < B.a->size()) swap(A, B);
  for (vector<int>::iterator it = B.a->begin(); it != B.a->end(); it++) {
    int y = *it;
    A.a->push_back(y);
    if (dep[x] - dep[lca] < K && dep[x] + dep[y] - 2 * dep[lca] >= K) {
      int ny = get_par(y, dep[x] + dep[y] - 2 * dep[lca] - K);
      int l = id[ny], r = id[ny] + sz[ny] - 1;
      pair<long long, long long> tmp = Query(A.T, 1, n, l, r);
      ans += (tmp.second - tmp.first * dep[ny] + tmp.first) * flag;
    }
  }
  B.a->clear();
  A.T = Merge(A.T, B.T);
  return A;
}
Block work(int x, int lca, int K, int flag) {
  Block cur;
  for (int i = 0; i < G[x].size(); i++) {
    int v = G[x][i];
    cur = Merge(cur, work(v, lca, K, flag), x, lca, K, flag);
  }
  for (int i = 0; i < has[x].size(); i++) {
    cur = Merge(cur, Block(has[x][i]), x, lca, K, flag);
  }
  return cur;
}
void solve(int K, int flag) {
  for (int i = 1; i <= n; i++) {
    if (dep[i] >= K) {
      int x = get_par(i, K - 1);
      int l = id[i], r = id[i] + sz[i] - 1, L = id[x] + sz[x], R = n;
      qry[id[x] - 1].push_back(make_pair(-(l - 1), QRY));
      qry[id[x] - 1].push_back(make_pair(r, QRY));
      qry[l - 1].push_back(make_pair(L - 1, QRY));
      qry[l - 1].push_back(make_pair(-R, QRY));
      qry[r].push_back(make_pair(-(L - 1), QRY));
      qry[r].push_back(make_pair(R, QRY));
      mul[QRY] = flag, QRY++;
    }
    for (int j = 0; j < all[i].size(); j++) {
      int k = j;
      node.clear();
      while (k < all[i].size() && all[i][j].first == all[i][k].first) {
        node.push_back(all[i][k].second.first);
        k++;
      }
      int rt = BuildTree();
      for (int l = j; l < k; l++) {
        has[all[i][l].second.first].push_back(all[i][l].second.second);
      }
      CUR = pool;
      work(rt, i, K, flag);
      j = k - 1;
    }
  }
}
int main() {
  int K, m;
  scanf("%d%d%d", &n, &m, &K);
  for (int i = 1; i < n; i++) {
    int x, y;
    scanf("%d%d", &x, &y);
    from[x].push_back(y);
    from[y].push_back(x);
  }
  dfs(1, 1), get_fa();
  for (int i = 1; i <= m; i++) {
    int x, y;
    scanf("%d%d", &x, &y);
    int lca = get_lca(x, y);
    if (x != lca && y != lca) {
      pair<int, int> t1 = make_pair(get_par(x, dep[x] - dep[lca] - 1),
                                    get_par(y, dep[y] - dep[lca] - 1));
      pair<int, int> t2 = make_pair(x, y);
      if (t1.first > t1.second)
        swap(t1.first, t1.second), swap(t2.first, t2.second);
      all[lca].push_back(make_pair(t1, t2));
    }
    x = id[x], y = id[y];
    if (x > y) swap(x, y);
    a[x].push_back(y);
  }
  for (int i = 1; i <= n; i++) {
    sort(all[i].begin(), all[i].end());
  }
  solve(K, 1), solve(K + 1, -1);
  for (int i = 1; i <= n; i++) {
    for (int j = 0; j < a[i].size(); j++) {
      modify(a[i][j], 1);
    }
    for (int j = 0; j < qry[i].size(); j++) {
      int x = qry[i][j].first;
      if (x >= 0)
        val[qry[i][j].second] += query(x);
      else
        val[qry[i][j].second] -= query(-x);
    }
  }
  for (int i = 0; i < QRY; i++) {
    ans += F(val[i]) * mul[i];
  }
  printf("%I64d\n", ans);
  return 0;
}
