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>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

using namespace std;
using namespace __gnu_pbds;
typedef
tree < pair<int,int>, null_type ,less<pair<int,int>>,
rb_tree_tag,
tree_order_statistics_node_update > ordered_set;

/*
    Operations :
    find_by_order : returns an iterator to the k-th
    largest element (counting from zero).
    order_of_key :  returns the number of items in
    a set that are strictly smaller than our item.
    # to use as a multiset just insert a pair<int,int> with unique second element, change
    int to pair<int,int> in typedef part
*/


struct euler_tree{
    vector<int> st, en;
    vector<vector<int>> g;
    int Timer, n;
    void init(int n_) {
        n = n_;
        g.assign(n+1, vector<int>());
        st.assign(n+1,0), en.assign(n+1,0);
        Timer = 0;
    }
    void addEdge(int u, int v) {
        g[u].push_back(v);
        g[v].push_back(u);
    }
    void dfs(int u, int p) {
        st[u] = ++Timer;
        for(int v : g[u]) {
            if(v == p) continue;
            dfs(v, u);
        }
        en[u] = Timer;
    }
    void prepare() {
        dfs(1, -1);
    }
    int subtree_sum(int u) {

    }
    int subtree_upd(int u) {

    }
}E;

struct compress_tree {

    vector<vector<int>> g, par;
    vector<int> depth, st, rev, en;
    int lg, n, Timer;

    void init(int n_) {
        n = n_;
        lg = 0;
        while((1<<lg) <= n) lg++;

        Timer = 0;
        depth.assign(n+1,0);
        st.assign(n+1,0);
        en.assign(n+1,0);
        g.assign(n+1,vector<int>());
        par.assign(n+1,vector<int>(lg, -1));
        rev.assign(n+1,0);
    }

    void addEdge(int u, int v) {
        g[u].push_back(v);
        g[v].push_back(u);
    }
    void dfs(int u, int p, int d) {
        depth[u] = d;
        par[u][0] = p;
        st[u] = ++Timer;
        for(int k = 1; k < lg; k++) {
            par[u][k] = (par[u][k-1] == -1 ? -1 : par[par[u][k-1]][k-1]);
        }
        for(int v : g[u]) {
            if(v == p) continue;
            dfs(v, u, d+1);
        }
        en[u] = Timer;
    }
    void prepare() {
        dfs(1, -1, 0);
    }
    int find_lca(int u, int v) {
        if(depth[u] < depth[v]) swap(u,v);
        int diff = depth[u] - depth[v];
        for (int i = lg-1; i >= 0; i--) {
            if (diff >= (1<<i)) {
                diff -= (1<<i);
                u = par[u][i];
            }
        }
        if(u == v) return u;
        for(int i = lg-1; i >= 0; i--) {
            if(par[u][i] != par[v][i]) {
                u = par[u][i];
                v = par[v][i];
            }
        }
        return par[v][0];
    }
    int kth_par(int u , int k) {
        if(k > depth[u]) return -1;
        for(int i = lg-1; i >= 0; i--) {
            if (k >= (1<<i)) {
                k -= (1<<i);
                u = par[u][i];
            }
        }
        return u;
    }
    /**
    Compress takes a subset
 	* of nodes and computes the minimal subtree that contains all the nodes
	* pairwise LCA's and compressing edges. Returns a list of (par, orig\_index)
	* representing a tree rooted at 0. The root points to itself.
    * Verification: USACO Debug the Bugs // not tested
 	* https://codeforces.com/contest/1320/problem/E // not tested
 	**/

    vector<pair<int,int>> compress(vector<int> nodes) {
        assert(nodes.size() > 0);
        auto cmp = [&](int a, int b) { return st[a] < st[b]; };
        sort(nodes.begin(), nodes.end(), cmp);
        int m = nodes.size();
        for(int i = 0; i+1 < m; i++) {
            int u = nodes[i], v = nodes[i+1];
            nodes.push_back(find_lca(u, v));
        }
        sort(nodes.begin(), nodes.end(), cmp);
        nodes.erase(unique(nodes.begin(), nodes.end()), nodes.end());
        for(int i = 0; i < nodes.size(); i++) rev[nodes[i]] = i;
        vector<pair<int,int>> ret;
        ret.push_back({0, nodes[0]});
        for(int i = 0; i+1 < nodes.size(); i++) ret.push_back({rev[find_lca(nodes[i], nodes[i+1])], nodes[i+1]});
        return ret;
    }
}L;

const int N = 1.5e5 + 100;
vector<int> G[N];
vector<tuple<int,int,int>> Q[N];
ordered_set Cnt;
long long ans;
int K;

// inclusive
int Range_Sum(int l, int r) {
    return Cnt.order_of_key({r+1,0}) - Cnt.order_of_key({l,0});
}
void dfs(int u, int p) {
    for(auto tt : Q[u]) {
        int x1 = get<0>(tt), y1 = get<1>(tt), idx = get<2>(tt);
        int d = L.depth[x1] - L.depth[u];
        if(d >= K) {
            int x2 = L.kth_par(x1, d-K);
            ans += Range_Sum(L.st[x2], L.en[x2]);
        }
        d = L.depth[y1] - L.depth[u];
        if(d >= K) {
            int y2 = L.kth_par(y1, d-K);
            ans += Range_Sum(L.st[y2], L.en[y2]);
        }
        Cnt.insert({L.st[x1],idx});
        Cnt.insert({L.st[y1],idx});
    }
    for(int v : G[u]) {
        if(v == p) continue;
        dfs(v, u);
    }
    for(auto tt : Q[u]) {
        int x1 = get<0>(tt), y1 = get<1>(tt), idx = get<2>(tt);
        Cnt.erase({L.st[x1],idx});
        Cnt.erase({L.st[y1],idx});
    }
}


vector<int> new_g[N];
int buck[N];
ordered_set y_time[N];
vector<tuple<int,int,int>> xs[N];
vector<pair<int,int>> new_tree;

void dsu_on_tree(int u, int p, int lca) {
//    cout << "u->  " << u << endl;
    for(int v : new_g[u]) {
        if(v == p) continue;
        dsu_on_tree(v, u, lca);
        if(u == 0) continue;
        if(xs[buck[u]].size() < xs[buck[v]].size()) swap(buck[u], buck[v]);
        for(auto t : xs[buck[v]]) {
            int x1 = get<0>(t), y1 = get<1>(t), idx = get<2>(t);
            int d = L.depth[new_tree[u].second] - L.depth[lca];
//            cout << "x1-> " << x1 << " u: " << u << endl;
//            for(auto tttt : y_time[buck[v]]) {
//                cout << tttt.first << " ";
//            }
//            cout << endl;

            if(d < K) {
//                cout << "here??" << endl;
                int baki = K-d;
//                cout << "baki: " << baki << endl;
                if(L.depth[y1]-L.depth[lca] >= baki) {
                    int z = L.kth_par(y1, L.depth[y1]-L.depth[lca]-baki);
                    int cnt = y_time[buck[u]].order_of_key({L.en[z]+1,0})-y_time[buck[u]].order_of_key({L.st[z], 0});
                    ans += cnt;
                }
            }
            else {
//                cout << "what?? " << endl;
                if(L.depth[y1]-L.depth[lca] >= K) {
                    int z = L.kth_par(y1, L.depth[y1]-L.depth[lca]-K);
                    int cnt = y_time[buck[u]].order_of_key({L.en[z]+1,0})-y_time[buck[u]].order_of_key({L.st[z], 0});
                    ans -= cnt;
                }
            }
        }
        for(auto t : xs[buck[v]]) xs[buck[u]].push_back(t);
        for(auto t : y_time[buck[v]]) y_time[buck[u]].insert(t);
    }
}

void solve(int u) {
    if(Q[u].size() <= 1) return;
    vector<int> nodes = {u};
    for(auto tt : Q[u]) {
        int x1 = get<0>(tt);
        nodes.push_back(x1);
    }
    new_tree = L.compress(nodes);
    int sz = (int)new_tree.size();

//    cout << "new_tree: " << endl;
//    for(auto t : new_tree) {
//        cout << t.first << " " << t.second << endl;
//    }
//    cout << "-----------------" << endl;


    for(int i = 0; i < sz; i++) buck[i] = i, y_time[i].clear(), xs[i].clear(), new_g[i].clear();
    for(int i = 1; i < new_tree.size(); i++) {
        new_g[new_tree[i].first].push_back(i);
    }
    for(auto tt : Q[u]) {
        int x1 = get<0>(tt), y1 = get<1>(tt), idx = get<2>(tt);
        int rev_x1 = L.rev[x1];
        xs[rev_x1].push_back(tt);
        int d = L.depth[x1] - L.depth[u];
//        cout << "d: " << d << endl;
        int lca = u;
        if(d < K) {
//            cout << "wahaha" << endl;
            int baki = K-d;
            if(L.depth[y1]-L.depth[u] >= baki) {
                int z = L.kth_par(y1, L.depth[y1]-L.depth[lca]-baki);
                int cnt = y_time[rev_x1].order_of_key({L.en[z]+1,0})-y_time[rev_x1].order_of_key({L.st[z], 0});
                if(rev_x1) ans += cnt;
            }
        }
        else {
//            cout << "here" << endl;
            if(L.depth[y1]-L.depth[lca] >= K) {
                int z = L.kth_par(y1, L.depth[y1]-L.depth[lca]-K);
                int cnt = y_time[rev_x1].order_of_key({L.en[z]+1,0})-y_time[rev_x1].order_of_key({L.st[z], 0});
                if(rev_x1) ans -= cnt;
            }
        }
        y_time[rev_x1].insert({L.st[y1], idx});
    }
    dsu_on_tree(0, -1, u);
}


int main() {
//    freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n, m;
    cin >> n >> m >> K;

    L.init(n);

    for(int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        L.addEdge(u, v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    L.prepare();
    for(int i = 1; i <= m; i++) {
        int s, t;
        cin >> s >> t;
        if(L.st[s] > L.st[t]) swap(s, t);
        int l = L.find_lca(s, t);
        Q[l].push_back(make_tuple(s,t,i));
    }
    dfs(1, -1);
//    cout << ans << endl;
//    cout << "ans-> " << ans << endl;
    for(int i = 1; i <= n; i++) {
        solve(i);
    }
//    cout << "ans: " << ans << endl;
    cout << ans << endl;
    return 0;
}

