import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

/*
Fools and Roads
192 E
http://codeforces.com/problemset/problem/192/E
LCA
 */

/*
5
1 2
1 3
2 4
2 5
2
1 4
3 5

5
3 4
4 5
1 4
2 4
3
2 3
1 3
3 5
 */
public class CF_192E implements Runnable {

  public static void main(String[] args) {
    new Thread(null, new CF_192E(), "", 1 << 25).start();
//    new CF_192E().run();
  }
  public void run() {
    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      PrintWriter out = new PrintWriter(System.out);

      int n = Integer.parseInt(in.readLine());
      HLD g = new HLD(n);
      int[][] edges = new int[2][n];
      for(int i = 0; i < n-1; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        edges[0][i] = v1;
        edges[1][i] = v2;

        g.add(v1, v2);
      }
//      g.goItr(0);
      g.go(0);

      int k = Integer.parseInt(in.readLine());
      for(int i = 0; i < k; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        g.update(v1, v2, 1);
      }

      for(int i = 0; i < n - 1; ++i) {
        out.print(g.count(edges[0][i], edges[1][i]) +" ");
      }
      out.println();

      out.close();
      in.close();
    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  //# Heavy-Light Decomposition
  //Decomposes a tree into heavy and light paths for logn path queries
  //in the tree. Can be used to find the lowest common ancestor as well
  //as to increment counts on nodes along the path between two nodes.
  //Uses: Create the HLD, add all the edges, run go(root). //$
  static class HLD {
   ArrayList<Integer>[] adj;
   int n;
   // hN - nodes sorted by heavy index, stores actual index.
   // hI - the heavy index in the tree.
   // hP - the heavyIndex of the node that starts the heavy path to this node.
   // lP - the heavyIndex of the node that is the parent of the heavyPath node.
   int[] h2V, v2H, hP, lP, size, par;
   SuperBIT bit;
   HLD(int N) {
     adj = new ArrayList[n = N];
     bit = new SuperBIT(n);
     for(int i = 0; i < n; ++i)
       adj[i] = new ArrayList<Integer>();
     h2V = new int[n]; v2H = new int[n];
     hP = new int[n]; lP = new int[n];
     size = new int[n];
     par = new int[n];
     Arrays.fill(size, 1);
     Arrays.fill(par, -1);
   }
   void add(int v1, int v2) {
     adj[v1].add(v2); adj[v2].add(v1);
   }

   static boolean getDepth;
   static int ind = 0;
   void go(int start) {
//     getDepth = true; dfs(start);
//     getDepth = false; dfs(start);
     dfs2(start); dfs3(start);
   }
   void dfs(int v) {
     if(!getDepth) {
       int p = par[v];
       v2H[v] = ind; h2V[ind] = v;
       if(p != -1 && v2H[p] == ind-1) {
         hP[v] = hP[p]; lP[v] = lP[p];
       } else {
         hP[v] = ind;
         if(p != -1) lP[v] = v2H[p];
         else lP[v] = ind;
       }
       ++ind;
     }
     int max = 0;
     for(int c = 0; c < adj[v].size(); ++c) {
       int u = adj[v].get(c);
       if(u == par[v]) continue;
       par[u] = v; dfs(u);
       if(getDepth) {
         size[v] += size[u];
         if(size[max] < size[u]) max = c;
       }
     }
     if(max > 0)
       Collections.swap(adj[v], 0, max);
   }

   void dfs2(int v) {
     int max = 0;
     for(int c = 0; c < adj[v].size(); ++c) {
       int u = adj[v].get(c);
       if(u == par[v]) continue;
       par[u] = v; dfs2(u);
       size[v] += size[u];
       if(size[max] < size[u]) max = c;
     }
     if(max > 0)
       Collections.swap(adj[v], 0, max);
   }
   void dfs3(int v) {
     {
       int p = par[v];
       v2H[v] = ind; h2V[ind] = v;
       if(p != -1 && v2H[p] == ind-1) {
         hP[v] = hP[p]; lP[v] = lP[p];
       } else {
         hP[v] = ind;
         if(p != -1) lP[v] = v2H[p];
         else lP[v] = ind;
       }
       ++ind;
     }
     for(int u : adj[v]) {
       if(u == par[v]) continue; dfs3(u);
     }
   }

   //#
   void goItr(int start) {
     dfsItr(start, true);
     dfsItr(start, false);
   }
   void dfsItr(int start, boolean getDepth) {
     int[] children = new int[n];
     ArrayDeque<Integer> st = new ArrayDeque<Integer>(n);
     st.add(start);
     int ind = 0;
     boolean cont = false;
     int[][] largestChild = null;
     if(getDepth)
       largestChild = new int[2][n];
     while(!st.isEmpty()) {
       int node = st.pop();
       int p = par[node];
       int child = children[node];
       int before = 0;
       if(child < 0) {
         child = -child -1;
         before = 1;
       }
       if(!getDepth && child == 0 && before == 0) {
         v2H[node] = ind;
         h2V[ind] = node;
         if(cont) {
           hP[h2V[ind]] = hP[h2V[ind-1]];
           lP[h2V[ind]] = lP[h2V[ind-1]];
         } else {
           cont = true;
           hP[h2V[ind]] = ind;
           if(p != -1) {
             lP[h2V[ind]] = v2H[p];
           } else {
             lP[h2V[ind]] = ind;
           }
         }
         ++ind;
       }
       if(before == 0)
         if(child < adj[node].size() && p == adj[node].get(child))
           ++child;
       if(child < adj[node].size()) {
         int next = adj[node].get(child);
         if(before == 0) {
           children[node] = -child-1;
           st.push(node);
           par[next] = node;
           st.push(next);
         } else {
           children[node] = 1+child;
           st.push(node);
           if(getDepth) {
             size[node] += size[next];
             if(largestChild[1][node] < size[next]) {
               largestChild[1][node] = size[next];
               largestChild[0][node] = child;
             }
           } else {
             cont = false;
           }
         }
       } else if(!getDepth) {
         cont = false;
       }
     }
     if(getDepth)
       for(int i = 0; i < n; ++i)
         if(largestChild[0][i] > 0)
           Collections.swap(adj[i], 0, largestChild[0][i]);
   }
   //$
   // Lowest common ancestor
   int lca(int v1, int v2) {
     int n1 = v2H[v1], n2 = v2H[v2];
     while(true) {
       if(hP[h2V[n1]] == hP[h2V[n2]])
         if(n1 < n2) return h2V[n1];
         else return h2V[n2];
       else if(n1 > n2) n1 = lP[h2V[n1]];
       else n2 = lP[h2V[n2]];
     }
   }
   // Count node values along the path from v1 to v2.
   long count(int v1, int v2) {
     int n1 = v2H[v1], n2 = v2H[v2];
     long ans = 0;
     while(true) {
       if(hP[h2V[n1]] == hP[h2V[n2]]) {
         if(n1 > n2) {
           ans += bit.query(n2+1, n1);
         } else if(n1 < n2) {
           ans += bit.query(n1+1, n2);
         }
         break;
       } else if(n1 > n2) {
         ans += bit.query(hP[h2V[n1]], n1);
         n1 = lP[h2V[n1]];
       } else {
         ans += bit.query(hP[h2V[n2]], n2);
         n2 = lP[h2V[n2]];
       }
     }
     return ans;
   }
   // Update every node along the path from v1 to v2 by the delta
   void update(int v1, int v2, long delta) {
     int n1 = v2H[v1], n2 = v2H[v2];
     while(true) {
       if(hP[h2V[n1]] == hP[h2V[n2]]) {
         if(n1 > n2) {
           bit.update(n2+1, n1, delta);
         } else if(n1 < n2) {
           bit.update(n1+1, n2, delta);
         }
         break;
       } else if(n1 > n2) {
         bit.update(hP[h2V[n1]], n1, delta);
         n1 = lP[h2V[n1]];
       } else {
         bit.update(hP[h2V[n2]], n2, delta);
         n2 = lP[h2V[n2]];
       }
     }
   }
  }
  //#
  static class SuperBIT {
   long[] dataMul, dataAdd;
   SuperBIT(int n) {
     dataMul = new long[n];
     dataAdd = new long[n];
   }
   void update(int left, int right, long val) {
     internalUpdate(left, val, -val * (left-1));
     internalUpdate(right, -val, val * right);
   }
   void internalUpdate(int at, long mul, long add) {
     while (at < dataMul.length) {
       dataMul[at] += mul;
       dataAdd[at] += add;
       at |= (at + 1);
     }
   }
   long query(int at) {
     long mul = 0;
     long add = 0;
     int start = at;
     while(at >= 0) {
       mul += dataMul[at];
       add += dataAdd[at];
       at = (at & (at + 1)) -1;
     }
     return mul * start + add;
   }
   long query(int left, int right) {
     if (left > right) {
       int temp = left;
       left = right;
       right = temp;
     }
     return query(right) - (left > 0 ? query(left-1) : 0);
   }
  }
  //$

}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

/*
Fools and Roads
192 E
http://codeforces.com/problemset/problem/192/E
LCA
 */

/*
5
1 2
1 3
2 4
2 5
2
1 4
3 5

5
3 4
4 5
1 4
2 4
3
2 3
1 3
3 5

 */
public class CF_192E implements Runnable {

  public static void main(String[] args) {
    new Thread(null, new CF_192E(), "", 1 << 25).start();
  }
  public void run() {
    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      PrintWriter out = new PrintWriter(System.out);

      int n = Integer.parseInt(in.readLine());
      HLD g = new HLD(n);
      int[][] edges = new int[2][n];
      for(int i = 0; i < n-1; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        edges[0][i] = v1;
        edges[1][i] = v2;

        g.add(v1, v2);
      }
      g.go(0);

      int k = Integer.parseInt(in.readLine());
      for(int i = 0; i < k; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        g.update(v1, v2, 1);
      }

      for(int i = 0; i < n - 1; ++i) {
        out.print(g.count(edges[0][i], edges[1][i]) +" ");
      }
      out.println();

      out.close();
      in.close();
    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  //# Heavy-Light Decomposition
  //Decomposes a tree into heavy and light paths for logn path queries
  //in the tree. Can be used to find the lowest common ancestor as well
  //as to increment counts on nodes along the path between two nodes.
  //Uses: Create the HLD, add all the edges, run go(root). //$
  static class HLD { // Stack Trick!!
   ArrayList<Integer>[] adj;
   int n;
   // h2v - heavy to node
   // v2h - node to heavy
   // hP - Heavy node that starts this node's heavy path.
   // lP - Heavy parent of hp[] for this node.
   int[] h2v, v2h, hp, lp, size, par;
   SuperBIT bit;
   HLD(int N) {
     adj = new ArrayList[n = N];
     bit = new SuperBIT(n);
     for(int i = 0; i < n; ++i)
       adj[i] = new ArrayList<Integer>();
     h2v = new int[n]; v2h = new int[n];
     hp = new int[n]; lp = new int[n];
     size = new int[n]; par = new int[n];
     Arrays.fill(size, 1);
     Arrays.fill(par, -1);
   }
   void add(int v1, int v2) {
     adj[v1].add(v2); adj[v2].add(v1);
   }

   void go(int s) {
     dfs(s);
     ArrayDeque<Integer> q = new ArrayDeque<>();
     q.add(s);
     int ind = lp[s] = hp[s] = 0;
     while(!q.isEmpty()) {
       int at = q.poll(), h = ind;
       if(at != s) lp[at] = v2h[par[at]];
       int l = lp[at];
       while(true) {
         for(int c = 1; c < adj[at].size(); ++c) {
           int u = adj[at].get(c);
           if(u == par[at]) continue;
           q.push(u);
         }
         lp[at] = l; hp[at] = h; h2v[ind] = at;
         v2h[at] = ind++;
         if(adj[at].size() <= 1 && at != s) break;
         at = adj[at].get(0);
       }
     }
   }
   void dfs(int v) {
     int max = 0, m = 0;
     for(int c = 0; c < adj[v].size(); ++c) {
       int u = adj[v].get(c);
       if(u == par[v]) continue;
       par[u] = v; dfs(u);
       int s = size[u]; size[v] += s;
       if(m < s) { max = c; m = s; }
     }
     if(max > 0)
       Collections.swap(adj[v], 0, max);
   }

   //#
   static boolean getDepth;
   static int ind = 0;
   void go2(int start) {
     getDepth = true; dfs2(start);
     getDepth = false; dfs2(start);
   }
   void dfs2(int v) {
     if(!getDepth) {
       int p = par[v];
       v2h[v] = ind; h2v[ind] = v;
       if(p != -1 && v2h[p] == ind-1) {
         hp[v] = hp[p]; lp[v] = lp[p];
       } else {
         hp[v] = ind;
         if(p != -1) lp[v] = v2h[p];
         else lp[v] = ind;
       }
       ++ind;
     }
     int max = 0, mm = 0;
     for(int c = 0; c < adj[v].size(); ++c) {
       int u = adj[v].get(c);
       if(u == par[v]) continue;
       par[u] = v; dfs2(u);
       if(getDepth) {
         int s = size[u]; size[v] += s;
         if(mm < s) { max = c; mm = s; }
       }
     }
     if(max > 0)
       Collections.swap(adj[v], 0, max);
   }

   void goItr(int start) {
     dfsItr(start, true);
     dfsItr(start, false);
   }
   void dfsItr(int start, boolean getDepth) {
     int[] children = new int[n];
     ArrayDeque<Integer> st = new ArrayDeque<Integer>(n);
     st.add(start);
     int ind = 0;
     boolean cont = false;
     int[][] largestChild = null;
     if(getDepth)
       largestChild = new int[2][n];
     while(!st.isEmpty()) {
       int node = st.pop();
       int p = par[node];
       int child = children[node];
       int before = 0;
       if(child < 0) {
         child = -child -1;
         before = 1;
       }
       if(!getDepth && child == 0 && before == 0) {
         v2h[node] = ind;
         h2v[ind] = node;
         if(cont) {
           hp[h2v[ind]] = hp[h2v[ind-1]];
           lp[h2v[ind]] = lp[h2v[ind-1]];
         } else {
           cont = true;
           hp[h2v[ind]] = ind;
           if(p != -1) {
             lp[h2v[ind]] = v2h[p];
           } else {
             lp[h2v[ind]] = ind;
           }
         }
         ++ind;
       }
       if(before == 0)
         if(child < adj[node].size() && p == adj[node].get(child))
           ++child;
       if(child < adj[node].size()) {
         int next = adj[node].get(child);
         if(before == 0) {
           children[node] = -child-1;
           st.push(node);
           par[next] = node;
           st.push(next);
         } else {
           children[node] = 1+child;
           st.push(node);
           if(getDepth) {
             size[node] += size[next];
             if(largestChild[1][node] < size[next]) {
               largestChild[1][node] = size[next];
               largestChild[0][node] = child;
             }
           } else {
             cont = false;
           }
         }
       } else if(!getDepth) {
         cont = false;
       }
     }
     if(getDepth)
       for(int i = 0; i < n; ++i)
         if(largestChild[0][i] > 0)
           Collections.swap(adj[i], 0, largestChild[0][i]);
   }
   //$
   // Lowest common ancestor
   int lca(int v1, int v2) {
     int n1 = v2h[v1], n2 = v2h[v2];
     while(true) {
       if(hp[h2v[n1]] == hp[h2v[n2]])
         if(n1 < n2) return h2v[n1];
         else return h2v[n2];
       else if(n1 > n2) n1 = lp[h2v[n1]];
       else n2 = lp[h2v[n2]];
     }
   }
   // Count node values along the path from v1 to v2.
   long count(int v1, int v2) {
     int n1 = v2h[v1], n2 = v2h[v2];
     long ans = 0;
     while(true) {
       if(hp[h2v[n1]] == hp[h2v[n2]]) {
         if(n1 > n2) {
           ans += bit.query(n2+1, n1);
         } else if(n1 < n2) {
           ans += bit.query(n1+1, n2);
         }
         break;
       } else if(n1 > n2) {
         ans += bit.query(hp[h2v[n1]], n1);
         n1 = lp[h2v[n1]];
       } else {
         ans += bit.query(hp[h2v[n2]], n2);
         n2 = lp[h2v[n2]];
       }
     }
     return ans;
   }
   // Update every node along the path from v1 to v2 by the delta
   void update(int v1, int v2, long delta) {
     int n1 = v2h[v1], n2 = v2h[v2];
     while(true) {
       if(hp[h2v[n1]] == hp[h2v[n2]]) {
         if(n1 > n2) {
           bit.update(n2+1, n1, delta);
         } else if(n1 < n2) {
           bit.update(n1+1, n2, delta);
         }
         break;
       } else if(n1 > n2) {
         bit.update(hp[h2v[n1]], n1, delta);
         n1 = lp[h2v[n1]];
       } else {
         bit.update(hp[h2v[n2]], n2, delta);
         n2 = lp[h2v[n2]];
       }
     }
   }
  }
  //#
  static class SuperBIT {
   long[] dataMul, dataAdd;
   SuperBIT(int n) {
     dataMul = new long[n];
     dataAdd = new long[n];
   }
   void update(int left, int right, long val) {
     internalUpdate(left, val, -val * (left-1));
     internalUpdate(right, -val, val * right);
   }
   void internalUpdate(int at, long mul, long add) {
     while (at < dataMul.length) {
       dataMul[at] += mul;
       dataAdd[at] += add;
       at |= (at + 1);
     }
   }
   long query(int at) {
     long mul = 0;
     long add = 0;
     int start = at;
     while(at >= 0) {
       mul += dataMul[at];
       add += dataAdd[at];
       at = (at & (at + 1)) -1;
     }
     return mul * start + add;
   }
   long query(int left, int right) {
     if (left > right) {
       int temp = left;
       left = right;
       right = temp;
     }
     return query(right) - (left > 0 ? query(left-1) : 0);
   }
  }
  //$

}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

/*
Fools and Roads
192 E
http://codeforces.com/problemset/problem/192/E
LCA
 */

/*
5
1 2
1 3
2 4
2 5
2
1 4
3 5

5
3 4
4 5
1 4
2 4
3
2 3
1 3
3 5
 */
public class CF_192E implements Runnable {

  public static void main(String[] args) {
    new Thread(null, new CF_192E(), "", 1 << 30).start();
//    new CF_192E().run();
  }
  public void run() {
    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      PrintWriter out = new PrintWriter(System.out);

      int n = Integer.parseInt(in.readLine());
      HLD g = new HLD(n);
      int[][] edges = new int[2][n];
      for(int i = 0; i < n-1; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        edges[0][i] = v1;
        edges[1][i] = v2;

        g.add(v1, v2);
      }
//      g.goItr(0);
      g.go(0);

      int k = Integer.parseInt(in.readLine());
      for(int i = 0; i < k; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        g.update(v1, v2, 1);
      }

      for(int i = 0; i < n - 1; ++i) {
        out.print(g.count(edges[0][i], edges[1][i]) +" ");
      }
      out.println();

      out.close();
      in.close();
    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  //# Heavy-Light Decomposition
  //Decomposes a tree into heavy and light paths for logn path queries
  //in the tree. Can be used to find the lowest common ancestor as well
  //as to increment counts on nodes along the path between two nodes.
  //Uses: Create the HLD, add all the edges, run go(root). //$
  static class HLD {
   ArrayList<Integer>[] adj;
   int n;
   // hN - nodes sorted by heavy index, stores actual index.
   // hI - the heavy index in the tree.
   // hP - the heavyIndex of the node that starts the heavy path to this node.
   // lP - the heavyIndex of the node that is the parent of the heavyPath node.
   int[] hN, hI, hP, lP, size, parents;
   SuperBIT bit;
   HLD(int N) {
     adj = new ArrayList[n = N];
     bit = new SuperBIT(n);
     for(int i = 0; i < n; ++i)
       adj[i] = new ArrayList<Integer>();
     hN = new int[n]; hI = new int[n];
     hP = new int[n]; lP = new int[n];
     size = new int[n];
     parents = new int[n];
     Arrays.fill(size, 1);
     Arrays.fill(parents, -1);
   }
   void add(int v1, int v2) {
     adj[v1].add(v2); adj[v2].add(v1);
   }

   static boolean getDepth;
   static int ind = 0;
   void go(int start) {
     getDepth = true; dfs(start);
     getDepth = false; dfs(start);
   }
   void dfs(int node) {
     int par = parents[node];
     if(!getDepth) {
       hI[node] = ind; hN[ind] = node;
       if(par != -1 && hI[par] == ind-1) {
         hP[node] = hP[par];
         lP[node] = lP[par];
       } else {
         hP[node] = ind;
         if(par != -1) lP[node] = hI[par];
         else lP[node] = ind;
       }
       ++ind;
     }
     int[] max = new int[] {-1, 0};
     int child = -1;
     for(int next : adj[node]) {
       ++child; if(next == par) continue;
       parents[next] = node; dfs(next);
       if(getDepth) {
         size[node] += size[next];
         if(max[0] == -1 || max[1] < size[next]) {
           max[0] = child; max[1] = size[next];
         }
       }
     }
     if(max[1] > 0)
       Collections.swap(adj[node], 0, max[0]);
   }

   //#
   void goItr(int start) {
     dfsItr(start, true);
     dfsItr(start, false);
   }
   void dfsItr(int start, boolean getDepth) {
     int[] children = new int[n];
     ArrayDeque<Integer> st = new ArrayDeque<Integer>(n);
     st.add(start);
     int ind = 0;
     boolean cont = false;
     int[][] largestChild = null;
     if(getDepth)
       largestChild = new int[2][n];
     while(!st.isEmpty()) {
       int node = st.pop();
       int par = parents[node];
       int child = children[node];
       int before = 0;
       if(child < 0) {
         child = -child -1;
         before = 1;
       }
       if(!getDepth && child == 0 && before == 0) {
         hI[node] = ind;
         hN[ind] = node;
         if(cont) {
           hP[hN[ind]] = hP[hN[ind-1]];
           lP[hN[ind]] = lP[hN[ind-1]];
         } else {
           cont = true;
           hP[hN[ind]] = ind;
           if(par != -1) {
             lP[hN[ind]] = hI[par];
           } else {
             lP[hN[ind]] = ind;
           }
         }
         ++ind;
       }
       if(before == 0)
         if(child < adj[node].size() && par == adj[node].get(child))
           ++child;
       if(child < adj[node].size()) {
         int next = adj[node].get(child);
         if(before == 0) {
           children[node] = -child-1;
           st.push(node);
           parents[next] = node;
           st.push(next);
         } else {
           children[node] = 1+child;
           st.push(node);
           if(getDepth) {
             size[node] += size[next];
             if(largestChild[1][node] < size[next]) {
               largestChild[1][node] = size[next];
               largestChild[0][node] = child;
             }
           } else {
             cont = false;
           }
         }
       } else if(!getDepth) {
         cont = false;
       }
     }
     if(getDepth)
       for(int i = 0; i < n; ++i)
         if(largestChild[0][i] > 0)
           Collections.swap(adj[i], 0, largestChild[0][i]);
   }
   //$
   // Lowest common ancestor
   int lca(int v1, int v2) {
     int n1 = hI[v1], n2 = hI[v2];
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]])
         if(n1 < n2) return hN[n1];
         else return hN[n2];
       else if(n1 > n2) n1 = lP[hN[n1]];
       else n2 = lP[hN[n2]];
     }
   }
   // Count node values along the path from v1 to v2.
   long count(int v1, int v2) {
     int n1 = hI[v1], n2 = hI[v2];
     long ans = 0;
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]]) {
         if(n1 > n2) {
           ans += bit.query(n2+1, n1);
         } else if(n1 < n2) {
           ans += bit.query(n1+1, n2);
         }
         break;
       } else if(n1 > n2) {
         ans += bit.query(hP[hN[n1]], n1);
         n1 = lP[hN[n1]];
       } else {
         ans += bit.query(hP[hN[n2]], n2);
         n2 = lP[hN[n2]];
       }
     }
     return ans;
   }
   // Update every node along the path from v1 to v2 by the delta
   void update(int v1, int v2, long delta) {
     int n1 = hI[v1], n2 = hI[v2];
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]]) {
         if(n1 > n2) {
           bit.update(n2+1, n1, delta);
         } else if(n1 < n2) {
           bit.update(n1+1, n2, delta);
         }
         break;
       } else if(n1 > n2) {
         bit.update(hP[hN[n1]], n1, delta);
         n1 = lP[hN[n1]];
       } else {
         bit.update(hP[hN[n2]], n2, delta);
         n2 = lP[hN[n2]];
       }
     }
   }
  }
  //#
  static class SuperBIT {
   long[] dataMul, dataAdd;
   SuperBIT(int n) {
     dataMul = new long[n];
     dataAdd = new long[n];
   }
   void update(int left, int right, long val) {
     internalUpdate(left, val, -val * (left-1));
     internalUpdate(right, -val, val * right);
   }
   void internalUpdate(int at, long mul, long add) {
     while (at < dataMul.length) {
       dataMul[at] += mul;
       dataAdd[at] += add;
       at |= (at + 1);
     }
   }
   long query(int at) {
     long mul = 0;
     long add = 0;
     int start = at;
     while(at >= 0) {
       mul += dataMul[at];
       add += dataAdd[at];
       at = (at & (at + 1)) -1;
     }
     return mul * start + add;
   }
   long query(int left, int right) {
     if (left > right) {
       int temp = left;
       left = right;
       right = temp;
     }
     return query(right) - (left > 0 ? query(left-1) : 0);
   }
  }
  //$

}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

/*
Fools and Roads
192 E
http://codeforces.com/problemset/problem/192/E
LCA
 */

/*
5
1 2
1 3
2 4
2 5
2
1 4
3 5

5
3 4
4 5
1 4
2 4
3
2 3
1 3
3 5
 */
public class CF_192E implements Runnable {

  public static void main(String[] args) {
    new Thread(null, new CF_192E(), "", 1 << 26).start();
//    new CF_192E().run();
  }
  public void run() {
    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      PrintWriter out = new PrintWriter(System.out);

      int n = Integer.parseInt(in.readLine());
      HLD g = new HLD(n);
      int[][] edges = new int[2][n];
      for(int i = 0; i < n-1; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        edges[0][i] = v1;
        edges[1][i] = v2;

        g.add(v1, v2);
      }
      g.goItr(0);
//      g.go(0);

      int k = Integer.parseInt(in.readLine());
      for(int i = 0; i < k; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        g.update(v1, v2, 1);
      }

      for(int i = 0; i < n - 1; ++i) {
        out.print(g.count(edges[0][i], edges[1][i]) +" ");
      }
      out.println();

      out.close();
      in.close();
    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  //# Heavy-Light Decomposition
  //Decomposes a tree into heavy and light paths for logn path queries
  //in the tree. Can be used to find the lowest common ancestor as well
  //as to increment counts on nodes along the path between two nodes.
  //Uses: Create the HLD, add all the edges, run go(root). //$
  static class HLD {
   ArrayList<Integer>[] adj;
   int n;
   // hN - nodes sorted by heavy index, stores actual index.
   // hI - the heavy index in the tree.
   // hP - the heavyIndex of the node that starts the heavy path to this node.
   // lP - the heavyIndex of the node that is the parent of the heavyPath node.
   int[] hN, hI, hP, lP, size, par;
   SuperBIT bit;
   HLD(int N) {
     adj = new ArrayList[n = N];
     bit = new SuperBIT(n);
     for(int i = 0; i < n; ++i)
       adj[i] = new ArrayList<Integer>();
     hN = new int[n]; hI = new int[n];
     hP = new int[n]; lP = new int[n];
     size = new int[n];
     par = new int[n];
     Arrays.fill(size, 1);
     Arrays.fill(par, -1);
   }
   void add(int v1, int v2) {
     adj[v1].add(v2); adj[v2].add(v1);
   }

   static boolean getDepth;
   static int ind = 0;
   void go(int start) {
     getDepth = true; dfs(start);
     getDepth = false; dfs(start);
   }
   void dfs(int v) {
     if(!getDepth) {
       int p = par[v];
       hI[v] = ind; hN[ind] = v;
       if(p != -1 && hI[p] == ind-1) {
         hP[v] = hP[p];
         lP[v] = lP[p];
       } else {
         hP[v] = ind;
         if(p != -1) lP[v] = hI[p];
         else lP[v] = ind;
       }
       ++ind;
     }
     int max = 0;
     for(int c = 0; c < adj[v].size(); ++c) {
       int u = adj[v].get(c);
       if(u == par[v]) continue;
       par[u] = v; dfs(u);
       if(getDepth) {
         size[v] += size[u];
         if(size[max] < size[u]) max = c;
       }
     }
     if(max > 0)
       Collections.swap(adj[v], 0, max);
   }

   //#
   void goItr(int start) {
     dfsItr(start, true);
     dfsItr(start, false);
   }
   void dfsItr(int start, boolean getDepth) {
     int[] children = new int[n];
     ArrayDeque<Integer> st = new ArrayDeque<Integer>(n);
     st.add(start);
     int ind = 0;
     boolean cont = false;
     int[][] largestChild = null;
     if(getDepth)
       largestChild = new int[2][n];
     while(!st.isEmpty()) {
       int node = st.pop();
       int p = par[node];
       int child = children[node];
       int before = 0;
       if(child < 0) {
         child = -child -1;
         before = 1;
       }
       if(!getDepth && child == 0 && before == 0) {
         hI[node] = ind;
         hN[ind] = node;
         if(cont) {
           hP[hN[ind]] = hP[hN[ind-1]];
           lP[hN[ind]] = lP[hN[ind-1]];
         } else {
           cont = true;
           hP[hN[ind]] = ind;
           if(p != -1) {
             lP[hN[ind]] = hI[p];
           } else {
             lP[hN[ind]] = ind;
           }
         }
         ++ind;
       }
       if(before == 0)
         if(child < adj[node].size() && p == adj[node].get(child))
           ++child;
       if(child < adj[node].size()) {
         int next = adj[node].get(child);
         if(before == 0) {
           children[node] = -child-1;
           st.push(node);
           par[next] = node;
           st.push(next);
         } else {
           children[node] = 1+child;
           st.push(node);
           if(getDepth) {
             size[node] += size[next];
             if(largestChild[1][node] < size[next]) {
               largestChild[1][node] = size[next];
               largestChild[0][node] = child;
             }
           } else {
             cont = false;
           }
         }
       } else if(!getDepth) {
         cont = false;
       }
     }
     if(getDepth)
       for(int i = 0; i < n; ++i)
         if(largestChild[0][i] > 0)
           Collections.swap(adj[i], 0, largestChild[0][i]);
   }
   //$
   // Lowest common ancestor
   int lca(int v1, int v2) {
     int n1 = hI[v1], n2 = hI[v2];
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]])
         if(n1 < n2) return hN[n1];
         else return hN[n2];
       else if(n1 > n2) n1 = lP[hN[n1]];
       else n2 = lP[hN[n2]];
     }
   }
   // Count node values along the path from v1 to v2.
   long count(int v1, int v2) {
     int n1 = hI[v1], n2 = hI[v2];
     long ans = 0;
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]]) {
         if(n1 > n2) {
           ans += bit.query(n2+1, n1);
         } else if(n1 < n2) {
           ans += bit.query(n1+1, n2);
         }
         break;
       } else if(n1 > n2) {
         ans += bit.query(hP[hN[n1]], n1);
         n1 = lP[hN[n1]];
       } else {
         ans += bit.query(hP[hN[n2]], n2);
         n2 = lP[hN[n2]];
       }
     }
     return ans;
   }
   // Update every node along the path from v1 to v2 by the delta
   void update(int v1, int v2, long delta) {
     int n1 = hI[v1], n2 = hI[v2];
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]]) {
         if(n1 > n2) {
           bit.update(n2+1, n1, delta);
         } else if(n1 < n2) {
           bit.update(n1+1, n2, delta);
         }
         break;
       } else if(n1 > n2) {
         bit.update(hP[hN[n1]], n1, delta);
         n1 = lP[hN[n1]];
       } else {
         bit.update(hP[hN[n2]], n2, delta);
         n2 = lP[hN[n2]];
       }
     }
   }
  }
  //#
  static class SuperBIT {
   long[] dataMul, dataAdd;
   SuperBIT(int n) {
     dataMul = new long[n];
     dataAdd = new long[n];
   }
   void update(int left, int right, long val) {
     internalUpdate(left, val, -val * (left-1));
     internalUpdate(right, -val, val * right);
   }
   void internalUpdate(int at, long mul, long add) {
     while (at < dataMul.length) {
       dataMul[at] += mul;
       dataAdd[at] += add;
       at |= (at + 1);
     }
   }
   long query(int at) {
     long mul = 0;
     long add = 0;
     int start = at;
     while(at >= 0) {
       mul += dataMul[at];
       add += dataAdd[at];
       at = (at & (at + 1)) -1;
     }
     return mul * start + add;
   }
   long query(int left, int right) {
     if (left > right) {
       int temp = left;
       left = right;
       right = temp;
     }
     return query(right) - (left > 0 ? query(left-1) : 0);
   }
  }
  //$

}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

/*
Fools and Roads
192 E
http://codeforces.com/problemset/problem/192/E
LCA
 */

/*
5
1 2
1 3
2 4
2 5
2
1 4
3 5

5
3 4
4 5
1 4
2 4
3
2 3
1 3
3 5
 */
public class CF_192E implements Runnable {

  public static void main(String[] args) {
    new Thread(null, new CF_192E(), "", 1 << 26).start();
//    new CF_192E().run();
  }
  public void run() {
    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      PrintWriter out = new PrintWriter(System.out);

      int n = Integer.parseInt(in.readLine());
      HLD g = new HLD(n);
      int[][] edges = new int[2][n];
      for(int i = 0; i < n-1; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        edges[0][i] = v1;
        edges[1][i] = v2;

        g.add(v1, v2);
      }
//      g.goItr(0);
      g.go(0);

      int k = Integer.parseInt(in.readLine());
      for(int i = 0; i < k; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        g.update(v1, v2, 1);
      }

      for(int i = 0; i < n - 1; ++i) {
        out.print(g.count(edges[0][i], edges[1][i]) +" ");
      }
      out.println();

      out.close();
      in.close();
    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  //# Heavy-Light Decomposition
  //Decomposes a tree into heavy and light paths for logn path queries
  //in the tree. Can be used to find the lowest common ancestor as well
  //as to increment counts on nodes along the path between two nodes.
  //Uses: Create the HLD, add all the edges, run go(root). //$
  static class HLD {
   ArrayList<Integer>[] adj;
   int n;
   // hN - nodes sorted by heavy index, stores actual index.
   // hI - the heavy index in the tree.
   // hP - the heavyIndex of the node that starts the heavy path to this node.
   // lP - the heavyIndex of the node that is the parent of the heavyPath node.
   int[] hN, hI, hP, lP, size, par;
   SuperBIT bit;
   HLD(int N) {
     adj = new ArrayList[n = N];
     bit = new SuperBIT(n);
     for(int i = 0; i < n; ++i)
       adj[i] = new ArrayList<Integer>();
     hN = new int[n]; hI = new int[n];
     hP = new int[n]; lP = new int[n];
     size = new int[n];
     par = new int[n];
     Arrays.fill(size, 1);
     Arrays.fill(par, -1);
   }
   void add(int v1, int v2) {
     adj[v1].add(v2); adj[v2].add(v1);
   }

   static boolean getDepth;
   static int ind = 0;
   void go(int start) {
     getDepth = true; dfs(start);
     getDepth = false; dfs(start);
   }
   void dfs(int v) {
     if(!getDepth) {
       int p = par[v];
       hI[v] = ind; hN[ind] = v;
       if(p != -1 && hI[p] == ind-1) {
         hP[v] = hP[p];
         lP[v] = lP[p];
       } else {
         hP[v] = ind;
         if(p != -1) lP[v] = hI[p];
         else lP[v] = ind;
       }
       ++ind;
     }
     int max = 0;
     for(int c = 0; c < adj[v].size(); ++c) {
       int u = adj[v].get(c);
       if(u == par[v]) continue;
       par[u] = v; dfs(u);
       if(getDepth) {
         size[v] += size[u];
         if(size[max] < size[u]) max = c;
       }
     }
     if(max > 0)
       Collections.swap(adj[v], 0, max);
   }

   //#
   void goItr(int start) {
     dfsItr(start, true);
     dfsItr(start, false);
   }
   void dfsItr(int start, boolean getDepth) {
     int[] children = new int[n];
     ArrayDeque<Integer> st = new ArrayDeque<Integer>(n);
     st.add(start);
     int ind = 0;
     boolean cont = false;
     int[][] largestChild = null;
     if(getDepth)
       largestChild = new int[2][n];
     while(!st.isEmpty()) {
       int node = st.pop();
       int p = par[node];
       int child = children[node];
       int before = 0;
       if(child < 0) {
         child = -child -1;
         before = 1;
       }
       if(!getDepth && child == 0 && before == 0) {
         hI[node] = ind;
         hN[ind] = node;
         if(cont) {
           hP[hN[ind]] = hP[hN[ind-1]];
           lP[hN[ind]] = lP[hN[ind-1]];
         } else {
           cont = true;
           hP[hN[ind]] = ind;
           if(p != -1) {
             lP[hN[ind]] = hI[p];
           } else {
             lP[hN[ind]] = ind;
           }
         }
         ++ind;
       }
       if(before == 0)
         if(child < adj[node].size() && p == adj[node].get(child))
           ++child;
       if(child < adj[node].size()) {
         int next = adj[node].get(child);
         if(before == 0) {
           children[node] = -child-1;
           st.push(node);
           par[next] = node;
           st.push(next);
         } else {
           children[node] = 1+child;
           st.push(node);
           if(getDepth) {
             size[node] += size[next];
             if(largestChild[1][node] < size[next]) {
               largestChild[1][node] = size[next];
               largestChild[0][node] = child;
             }
           } else {
             cont = false;
           }
         }
       } else if(!getDepth) {
         cont = false;
       }
     }
     if(getDepth)
       for(int i = 0; i < n; ++i)
         if(largestChild[0][i] > 0)
           Collections.swap(adj[i], 0, largestChild[0][i]);
   }
   //$
   // Lowest common ancestor
   int lca(int v1, int v2) {
     int n1 = hI[v1], n2 = hI[v2];
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]])
         if(n1 < n2) return hN[n1];
         else return hN[n2];
       else if(n1 > n2) n1 = lP[hN[n1]];
       else n2 = lP[hN[n2]];
     }
   }
   // Count node values along the path from v1 to v2.
   long count(int v1, int v2) {
     int n1 = hI[v1], n2 = hI[v2];
     long ans = 0;
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]]) {
         if(n1 > n2) {
           ans += bit.query(n2+1, n1);
         } else if(n1 < n2) {
           ans += bit.query(n1+1, n2);
         }
         break;
       } else if(n1 > n2) {
         ans += bit.query(hP[hN[n1]], n1);
         n1 = lP[hN[n1]];
       } else {
         ans += bit.query(hP[hN[n2]], n2);
         n2 = lP[hN[n2]];
       }
     }
     return ans;
   }
   // Update every node along the path from v1 to v2 by the delta
   void update(int v1, int v2, long delta) {
     int n1 = hI[v1], n2 = hI[v2];
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]]) {
         if(n1 > n2) {
           bit.update(n2+1, n1, delta);
         } else if(n1 < n2) {
           bit.update(n1+1, n2, delta);
         }
         break;
       } else if(n1 > n2) {
         bit.update(hP[hN[n1]], n1, delta);
         n1 = lP[hN[n1]];
       } else {
         bit.update(hP[hN[n2]], n2, delta);
         n2 = lP[hN[n2]];
       }
     }
   }
  }
  //#
  static class SuperBIT {
   long[] dataMul, dataAdd;
   SuperBIT(int n) {
     dataMul = new long[n];
     dataAdd = new long[n];
   }
   void update(int left, int right, long val) {
     internalUpdate(left, val, -val * (left-1));
     internalUpdate(right, -val, val * right);
   }
   void internalUpdate(int at, long mul, long add) {
     while (at < dataMul.length) {
       dataMul[at] += mul;
       dataAdd[at] += add;
       at |= (at + 1);
     }
   }
   long query(int at) {
     long mul = 0;
     long add = 0;
     int start = at;
     while(at >= 0) {
       mul += dataMul[at];
       add += dataAdd[at];
       at = (at & (at + 1)) -1;
     }
     return mul * start + add;
   }
   long query(int left, int right) {
     if (left > right) {
       int temp = left;
       left = right;
       right = temp;
     }
     return query(right) - (left > 0 ? query(left-1) : 0);
   }
  }
  //$

}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

/*
Fools and Roads
192 E
http://codeforces.com/problemset/problem/192/E
LCA
 */

/*
5
1 2
1 3
2 4
2 5
2
1 4
3 5

5
3 4
4 5
1 4
2 4
3
2 3
1 3
3 5
 */
public class CF_192E implements Runnable {

  public static void main(String[] args) {
    new Thread(null, new CF_192E(), "", 1 << 30).start();
//    new CF_192E().run();
  }
  public void run() {
    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      PrintWriter out = new PrintWriter(System.out);

      int n = Integer.parseInt(in.readLine());
      HLD g = new HLD(n);
      int[][] edges = new int[2][n];
      for(int i = 0; i < n-1; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        edges[0][i] = v1;
        edges[1][i] = v2;

        g.add(v1, v2);
      }
//      g.goItr(0);
      g.go(0);

      int k = Integer.parseInt(in.readLine());
      for(int i = 0; i < k; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        g.update(v1, v2, 1);
      }

      for(int i = 0; i < n - 1; ++i) {
        out.print(g.count(edges[0][i], edges[1][i]) +" ");
      }
      out.println();

      out.close();
      in.close();
    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  //# Heavy-Light Decomposition
  //Decomposes a tree into heavy and light paths for logn path queries
  //in the tree. Can be used to find the lowest common ancestor as well
  //as to increment counts on nodes along the path between two nodes.
  //Uses: Create the HLD, add all the edges, run go(root). //$
  static class HLD {
   ArrayList<Integer>[] adj;
   int n;
   // hN - nodes sorted by heavy index, stores actual index.
   // hI - the heavy index in the tree.
   // hP - the heavyIndex of the node that starts the heavy path to this node.
   // lP - the heavyIndex of the node that is the parent of the heavyPath node.
   int[] hN, hI, hP, lP, size, par;
   SuperBIT bit;
   HLD(int N) {
     adj = new ArrayList[n = N];
     bit = new SuperBIT(n);
     for(int i = 0; i < n; ++i)
       adj[i] = new ArrayList<Integer>();
     hN = new int[n]; hI = new int[n];
     hP = new int[n]; lP = new int[n];
     size = new int[n];
     par = new int[n];
     Arrays.fill(size, 1);
     Arrays.fill(par, -1);
   }
   void add(int v1, int v2) {
     adj[v1].add(v2); adj[v2].add(v1);
   }

   static boolean getDepth;
   static int ind = 0;
   void go(int start) {
     getDepth = true; dfs(start);
     getDepth = false; dfs(start);
   }
   void dfs(int v) {
     if(!getDepth) {
       int p = par[v];
       hI[v] = ind; hN[ind] = v;
       if(p != -1 && hI[p] == ind-1) {
         hP[v] = hP[p];
         lP[v] = lP[p];
       } else {
         hP[v] = ind;
         if(p != -1) lP[v] = hI[p];
         else lP[v] = ind;
       }
       ++ind;
     }
     int max = 0;
     for(int c = 0; c < adj[v].size(); ++c) {
       int u = adj[v].get(c);
       if(u == par[v]) continue;
       par[u] = v; dfs(u);
       if(getDepth) {
         size[v] += size[u];
         if(size[max] < size[u]) max = c;
       }
     }
     if(max > 0)
       Collections.swap(adj[v], 0, max);
   }

   //#
   void goItr(int start) {
     dfsItr(start, true);
     dfsItr(start, false);
   }
   void dfsItr(int start, boolean getDepth) {
     int[] children = new int[n];
     ArrayDeque<Integer> st = new ArrayDeque<Integer>(n);
     st.add(start);
     int ind = 0;
     boolean cont = false;
     int[][] largestChild = null;
     if(getDepth)
       largestChild = new int[2][n];
     while(!st.isEmpty()) {
       int node = st.pop();
       int p = par[node];
       int child = children[node];
       int before = 0;
       if(child < 0) {
         child = -child -1;
         before = 1;
       }
       if(!getDepth && child == 0 && before == 0) {
         hI[node] = ind;
         hN[ind] = node;
         if(cont) {
           hP[hN[ind]] = hP[hN[ind-1]];
           lP[hN[ind]] = lP[hN[ind-1]];
         } else {
           cont = true;
           hP[hN[ind]] = ind;
           if(p != -1) {
             lP[hN[ind]] = hI[p];
           } else {
             lP[hN[ind]] = ind;
           }
         }
         ++ind;
       }
       if(before == 0)
         if(child < adj[node].size() && p == adj[node].get(child))
           ++child;
       if(child < adj[node].size()) {
         int next = adj[node].get(child);
         if(before == 0) {
           children[node] = -child-1;
           st.push(node);
           par[next] = node;
           st.push(next);
         } else {
           children[node] = 1+child;
           st.push(node);
           if(getDepth) {
             size[node] += size[next];
             if(largestChild[1][node] < size[next]) {
               largestChild[1][node] = size[next];
               largestChild[0][node] = child;
             }
           } else {
             cont = false;
           }
         }
       } else if(!getDepth) {
         cont = false;
       }
     }
     if(getDepth)
       for(int i = 0; i < n; ++i)
         if(largestChild[0][i] > 0)
           Collections.swap(adj[i], 0, largestChild[0][i]);
   }
   //$
   // Lowest common ancestor
   int lca(int v1, int v2) {
     int n1 = hI[v1], n2 = hI[v2];
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]])
         if(n1 < n2) return hN[n1];
         else return hN[n2];
       else if(n1 > n2) n1 = lP[hN[n1]];
       else n2 = lP[hN[n2]];
     }
   }
   // Count node values along the path from v1 to v2.
   long count(int v1, int v2) {
     int n1 = hI[v1], n2 = hI[v2];
     long ans = 0;
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]]) {
         if(n1 > n2) {
           ans += bit.query(n2+1, n1);
         } else if(n1 < n2) {
           ans += bit.query(n1+1, n2);
         }
         break;
       } else if(n1 > n2) {
         ans += bit.query(hP[hN[n1]], n1);
         n1 = lP[hN[n1]];
       } else {
         ans += bit.query(hP[hN[n2]], n2);
         n2 = lP[hN[n2]];
       }
     }
     return ans;
   }
   // Update every node along the path from v1 to v2 by the delta
   void update(int v1, int v2, long delta) {
     int n1 = hI[v1], n2 = hI[v2];
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]]) {
         if(n1 > n2) {
           bit.update(n2+1, n1, delta);
         } else if(n1 < n2) {
           bit.update(n1+1, n2, delta);
         }
         break;
       } else if(n1 > n2) {
         bit.update(hP[hN[n1]], n1, delta);
         n1 = lP[hN[n1]];
       } else {
         bit.update(hP[hN[n2]], n2, delta);
         n2 = lP[hN[n2]];
       }
     }
   }
  }
  //#
  static class SuperBIT {
   long[] dataMul, dataAdd;
   SuperBIT(int n) {
     dataMul = new long[n];
     dataAdd = new long[n];
   }
   void update(int left, int right, long val) {
     internalUpdate(left, val, -val * (left-1));
     internalUpdate(right, -val, val * right);
   }
   void internalUpdate(int at, long mul, long add) {
     while (at < dataMul.length) {
       dataMul[at] += mul;
       dataAdd[at] += add;
       at |= (at + 1);
     }
   }
   long query(int at) {
     long mul = 0;
     long add = 0;
     int start = at;
     while(at >= 0) {
       mul += dataMul[at];
       add += dataAdd[at];
       at = (at & (at + 1)) -1;
     }
     return mul * start + add;
   }
   long query(int left, int right) {
     if (left > right) {
       int temp = left;
       left = right;
       right = temp;
     }
     return query(right) - (left > 0 ? query(left-1) : 0);
   }
  }
  //$

}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

/*
Fools and Roads
192 E
http://codeforces.com/problemset/problem/192/E
LCA
 */

/*
5
1 2
1 3
2 4
2 5
2
1 4
3 5

5
3 4
4 5
1 4
2 4
3
2 3
1 3
3 5

 */
public class CF_192E implements Runnable {

  public static void main(String[] args) {
    new Thread(null, new CF_192E(), "", 1 << 25).start();
//    new CF_192E().run();
  }
  public void run() {
    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      PrintWriter out = new PrintWriter(System.out);

      int n = Integer.parseInt(in.readLine());
      HLD g = new HLD(n);
      int[][] edges = new int[2][n];
      for(int i = 0; i < n-1; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        edges[0][i] = v1;
        edges[1][i] = v2;

        g.add(v1, v2);
      }
//      g.goItr(0);
      g.go(0);
//      System.out.println(Arrays.toString(g.v2H));
//      System.out.println(Arrays.toString(g.h2V));
//      System.out.println(Arrays.toString(g.hP));
//      System.out.println(Arrays.toString(g.lP));

      int k = Integer.parseInt(in.readLine());
      for(int i = 0; i < k; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        g.update(v1, v2, 1);
      }

      for(int i = 0; i < n - 1; ++i) {
        out.print(g.count(edges[0][i], edges[1][i]) +" ");
      }
      out.println();

      out.close();
      in.close();
    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  //# Heavy-Light Decomposition
  //Decomposes a tree into heavy and light paths for logn path queries
  //in the tree. Can be used to find the lowest common ancestor as well
  //as to increment counts on nodes along the path between two nodes.
  //Uses: Create the HLD, add all the edges, run go(root). //$
  static class HLD {
   ArrayList<Integer>[] adj;
   int n;
   // hN - nodes sorted by heavy index, stores actual index.
   // hI - the heavy index in the tree.
   // hP - the heavyIndex of the node that starts the heavy path to this node.
   // lP - the heavyIndex of the node that is the parent of the heavyPath node.
   int[] h2V, v2H, hP, lP, size, par;
   SuperBIT bit;
   HLD(int N) {
     adj = new ArrayList[n = N];
     bit = new SuperBIT(n);
     for(int i = 0; i < n; ++i)
       adj[i] = new ArrayList<Integer>();
     h2V = new int[n]; v2H = new int[n];
     hP = new int[n]; lP = new int[n];
     size = new int[n];
     par = new int[n];
     Arrays.fill(size, 1);
     Arrays.fill(par, -1);
   }
   void add(int v1, int v2) {
     adj[v1].add(v2); adj[v2].add(v1);
   }

   static boolean getDepth;
   static int ind = 0;
   void go(int start) {
//     getDepth = true; dfs(start);
//     getDepth = false; dfs(start);
     dfs2(start);//  dfs3(start);

     ArrayDeque<Integer> q = new ArrayDeque<Integer>();
     q.add(start);
     int ind = lP[start] = hP[start] = 0;
     while(!q.isEmpty()) {
       int at = q.poll();
       int h = ind;
       if(par[at] != -1) lP[at] = v2H[par[at]];
       int l = lP[at];
       while(true) {
         for(int c = adj[at].size()-1; c > 0; --c) {
           int u = adj[at].get(c);
           if(u == par[at]) continue;
           q.push(u);
         }
         lP[at] = l; hP[at] = h; h2V[ind] = at;
         v2H[at] = ind++;
         if(adj[at].size() <= 1 && at != start) break;
         at = adj[at].get(0);
       }
     }
   }
   void dfs2(int v) {
     int max = 0, m = 0;
     for(int c = 0; c < adj[v].size(); ++c) {
       int u = adj[v].get(c);
       if(u == par[v]) continue;
       par[u] = v; dfs2(u);
       int s = size[u]; size[v] += s;
       if(m < s) { max = c; m = s; }
     }
     if(max > 0)
       Collections.swap(adj[v], 0, max);
   }

   void dfs(int v) {
     if(!getDepth) {
       int p = par[v];
       v2H[v] = ind; h2V[ind] = v;
       if(p != -1 && v2H[p] == ind-1) {
         hP[v] = hP[p]; lP[v] = lP[p];
       } else {
         hP[v] = ind;
         if(p != -1) lP[v] = v2H[p];
         else lP[v] = ind;
       }
       ++ind;
     }
     int max = 0, mm = 0;
     for(int c = 0; c < adj[v].size(); ++c) {
       int u = adj[v].get(c);
       if(u == par[v]) continue;
       par[u] = v; dfs(u);
       if(getDepth) {
         int s = size[u]; size[v] += s;
         if(mm < s) { max = c; mm = s; }
       }
     }
     if(max > 0)
       Collections.swap(adj[v], 0, max);
   }

   void dfs3(int v) {
     {
       int p = par[v];
       v2H[v] = ind; h2V[ind] = v;
       if(p != -1 && v2H[p] == ind-1) {
         hP[v] = hP[p]; lP[v] = lP[p];
       } else {
         hP[v] = ind;
         if(p != -1) lP[v] = v2H[p];
         else lP[v] = ind;
       }
       ++ind;
     }
     for(int u : adj[v]) {
       if(u == par[v]) continue; dfs3(u);
     }
   }

   //#
   void goItr(int start) {
     dfsItr(start, true);
     dfsItr(start, false);
   }
   void dfsItr(int start, boolean getDepth) {
     int[] children = new int[n];
     ArrayDeque<Integer> st = new ArrayDeque<Integer>(n);
     st.add(start);
     int ind = 0;
     boolean cont = false;
     int[][] largestChild = null;
     if(getDepth)
       largestChild = new int[2][n];
     while(!st.isEmpty()) {
       int node = st.pop();
       int p = par[node];
       int child = children[node];
       int before = 0;
       if(child < 0) {
         child = -child -1;
         before = 1;
       }
       if(!getDepth && child == 0 && before == 0) {
         v2H[node] = ind;
         h2V[ind] = node;
         if(cont) {
           hP[h2V[ind]] = hP[h2V[ind-1]];
           lP[h2V[ind]] = lP[h2V[ind-1]];
         } else {
           cont = true;
           hP[h2V[ind]] = ind;
           if(p != -1) {
             lP[h2V[ind]] = v2H[p];
           } else {
             lP[h2V[ind]] = ind;
           }
         }
         ++ind;
       }
       if(before == 0)
         if(child < adj[node].size() && p == adj[node].get(child))
           ++child;
       if(child < adj[node].size()) {
         int next = adj[node].get(child);
         if(before == 0) {
           children[node] = -child-1;
           st.push(node);
           par[next] = node;
           st.push(next);
         } else {
           children[node] = 1+child;
           st.push(node);
           if(getDepth) {
             size[node] += size[next];
             if(largestChild[1][node] < size[next]) {
               largestChild[1][node] = size[next];
               largestChild[0][node] = child;
             }
           } else {
             cont = false;
           }
         }
       } else if(!getDepth) {
         cont = false;
       }
     }
     if(getDepth)
       for(int i = 0; i < n; ++i)
         if(largestChild[0][i] > 0)
           Collections.swap(adj[i], 0, largestChild[0][i]);
   }
   //$
   // Lowest common ancestor
   int lca(int v1, int v2) {
     int n1 = v2H[v1], n2 = v2H[v2];
     while(true) {
       if(hP[h2V[n1]] == hP[h2V[n2]])
         if(n1 < n2) return h2V[n1];
         else return h2V[n2];
       else if(n1 > n2) n1 = lP[h2V[n1]];
       else n2 = lP[h2V[n2]];
     }
   }
   // Count node values along the path from v1 to v2.
   long count(int v1, int v2) {
     int n1 = v2H[v1], n2 = v2H[v2];
     long ans = 0;
     while(true) {
       if(hP[h2V[n1]] == hP[h2V[n2]]) {
         if(n1 > n2) {
           ans += bit.query(n2+1, n1);
         } else if(n1 < n2) {
           ans += bit.query(n1+1, n2);
         }
         break;
       } else if(n1 > n2) {
         ans += bit.query(hP[h2V[n1]], n1);
         n1 = lP[h2V[n1]];
       } else {
         ans += bit.query(hP[h2V[n2]], n2);
         n2 = lP[h2V[n2]];
       }
     }
     return ans;
   }
   // Update every node along the path from v1 to v2 by the delta
   void update(int v1, int v2, long delta) {
     int n1 = v2H[v1], n2 = v2H[v2];
     while(true) {
       if(hP[h2V[n1]] == hP[h2V[n2]]) {
         if(n1 > n2) {
           bit.update(n2+1, n1, delta);
         } else if(n1 < n2) {
           bit.update(n1+1, n2, delta);
         }
         break;
       } else if(n1 > n2) {
         bit.update(hP[h2V[n1]], n1, delta);
         n1 = lP[h2V[n1]];
       } else {
         bit.update(hP[h2V[n2]], n2, delta);
         n2 = lP[h2V[n2]];
       }
     }
   }
  }
  //#
  static class SuperBIT {
   long[] dataMul, dataAdd;
   SuperBIT(int n) {
     dataMul = new long[n];
     dataAdd = new long[n];
   }
   void update(int left, int right, long val) {
     internalUpdate(left, val, -val * (left-1));
     internalUpdate(right, -val, val * right);
   }
   void internalUpdate(int at, long mul, long add) {
     while (at < dataMul.length) {
       dataMul[at] += mul;
       dataAdd[at] += add;
       at |= (at + 1);
     }
   }
   long query(int at) {
     long mul = 0;
     long add = 0;
     int start = at;
     while(at >= 0) {
       mul += dataMul[at];
       add += dataAdd[at];
       at = (at & (at + 1)) -1;
     }
     return mul * start + add;
   }
   long query(int left, int right) {
     if (left > right) {
       int temp = left;
       left = right;
       right = temp;
     }
     return query(right) - (left > 0 ? query(left-1) : 0);
   }
  }
  //$

}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

/*
Fools and Roads
192 E
http://codeforces.com/problemset/problem/192/E
LCA
 */

/*
5
1 2
1 3
2 4
2 5
2
1 4
3 5

5
3 4
4 5
1 4
2 4
3
2 3
1 3
3 5
 */
public class CF_192E implements Runnable {

  public static void main(String[] args) {
//    new Thread(null, new CF_192E(), "", 1 << 30).start();
    new CF_192E().run();
  }
  public void run() {
    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      PrintWriter out = new PrintWriter(System.out);

      int n = Integer.parseInt(in.readLine());
      HLD g = new HLD(n);
      int[][] edges = new int[2][n];
      for(int i = 0; i < n-1; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        edges[0][i] = v1;
        edges[1][i] = v2;

        g.add(v1, v2);
      }
      g.goItr(0);

      int k = Integer.parseInt(in.readLine());
      for(int i = 0; i < k; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        g.update(v1, v2, 1);
      }

      for(int i = 0; i < n - 1; ++i) {
        out.print(g.count(edges[0][i], edges[1][i]) +" ");
      }
      out.println();

      out.close();
      in.close();
    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  //# Heavy-Light Decomposition
  //Decomposes a tree into heavy and light paths for logn path queries
  //in the tree. Can be used to find the lowest common ancestor as well
  //as to increment counts on nodes along the path between two nodes.
  //Uses: Create the HLD, add all the edges, run go(root). //$
  static class HLD {
   ArrayList<Integer>[] adj;
   int n;
   // hN - nodes sorted by heavy index, stores actual index.
   // hI - the heavy index in the tree.
   // hP - the heavyIndex of the node that starts the heavy path to this node.
   // lP - the heavyIndex of the node that is the parent of the heavyPath node.
   int[] hN, hI, hP, lP, size, parents;
   SuperBIT bit;
   HLD(int N) {
     adj = new ArrayList[n = N];
     bit = new SuperBIT(n);
     for(int i = 0; i < n; ++i)
       adj[i] = new ArrayList<Integer>();
     hN = new int[n]; hI = new int[n];
     hP = new int[n]; lP = new int[n];
     size = new int[n];
     parents = new int[n];
     Arrays.fill(size, 1);
     Arrays.fill(parents, -1);
   }
   void add(int v1, int v2) {
     adj[v1].add(v2); adj[v2].add(v1);
   }

   static boolean getDepth;
   static int ind = 0;
   void go(int start) {
     getDepth = true; dfs(start);
     getDepth = false; dfs(start);
   }
   void dfs(int node) {
     int par = parents[node];
     if(!getDepth) {
       hI[node] = ind; hN[ind] = node;
       if(par != -1 && hI[par] == ind-1) {
         hP[node] = hP[par];
         lP[node] = lP[par];
       } else {
         hP[node] = ind;
         if(par != -1) lP[node] = hI[par];
         else lP[node] = ind;
       }
       ++ind;
     }
     int[] max = new int[] {-1, 0};
     int child = -1;
     for(int next : adj[node]) {
       ++child; if(next == par) continue;
       parents[next] = node; dfs(next);
       if(getDepth) {
         size[node] += size[next];
         if(max[0] == -1 || max[1] < size[next]) {
           max[0] = child; max[1] = size[next];
         }
       }
     }
     if(max[1] > 0)
       Collections.swap(adj[node], 0, max[0]);
   }

   //#
   void goItr(int start) {
     dfsItr(start, true);
     dfsItr(start, false);
   }
   void dfsItr(int start, boolean getDepth) {
     int[] children = new int[n];
     ArrayDeque<Integer> st = new ArrayDeque<Integer>(n);
     st.add(start);
     int ind = 0;
     boolean cont = false;
     int[][] largestChild = null;
     if(getDepth)
       largestChild = new int[2][n];
     while(!st.isEmpty()) {
       int node = st.pop();
       int par = parents[node];
       int child = children[node];
       int before = 0;
       if(child < 0) {
         child = -child -1;
         before = 1;
       }
       if(!getDepth && child == 0 && before == 0) {
         hI[node] = ind;
         hN[ind] = node;
         if(cont) {
           hP[hN[ind]] = hP[hN[ind-1]];
           lP[hN[ind]] = lP[hN[ind-1]];
         } else {
           cont = true;
           hP[hN[ind]] = ind;
           if(par != -1) {
             lP[hN[ind]] = hI[par];
           } else {
             lP[hN[ind]] = ind;
           }
         }
         ++ind;
       }
       if(before == 0)
         if(child < adj[node].size() && par == adj[node].get(child))
           ++child;
       if(child < adj[node].size()) {
         int next = adj[node].get(child);
         if(before == 0) {
           children[node] = -child-1;
           st.push(node);
           parents[next] = node;
           st.push(next);
         } else {
           children[node] = 1+child;
           st.push(node);
           if(getDepth) {
             size[node] += size[next];
             if(largestChild[1][node] < size[next]) {
               largestChild[1][node] = size[next];
               largestChild[0][node] = child;
             }
           } else {
             cont = false;
           }
         }
       } else if(!getDepth) {
         cont = false;
       }
     }
     if(getDepth)
       for(int i = 0; i < n; ++i)
         if(largestChild[0][i] > 0)
           Collections.swap(adj[i], 0, largestChild[0][i]);
   }
   //$
   // Lowest common ancestor
   int lca(int v1, int v2) {
     int n1 = hI[v1], n2 = hI[v2];
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]])
         if(n1 < n2) return hN[n1];
         else return hN[n2];
       else if(n1 > n2) n1 = lP[hN[n1]];
       else n2 = lP[hN[n2]];
     }
   }
   // Count node values along the path from v1 to v2.
   long count(int v1, int v2) {
     int n1 = hI[v1], n2 = hI[v2];
     long ans = 0;
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]]) {
         if(n1 > n2) {
           ans += bit.query(n2+1, n1);
         } else if(n1 < n2) {
           ans += bit.query(n1+1, n2);
         }
         break;
       } else if(n1 > n2) {
         ans += bit.query(hP[hN[n1]], n1);
         n1 = lP[hN[n1]];
       } else {
         ans += bit.query(hP[hN[n2]], n2);
         n2 = lP[hN[n2]];
       }
     }
     return ans;
   }
   // Update every node along the path from v1 to v2 by the delta
   void update(int v1, int v2, long delta) {
     int n1 = hI[v1], n2 = hI[v2];
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]]) {
         if(n1 > n2) {
           bit.update(n2+1, n1, delta);
         } else if(n1 < n2) {
           bit.update(n1+1, n2, delta);
         }
         break;
       } else if(n1 > n2) {
         bit.update(hP[hN[n1]], n1, delta);
         n1 = lP[hN[n1]];
       } else {
         bit.update(hP[hN[n2]], n2, delta);
         n2 = lP[hN[n2]];
       }
     }
   }
  }
  //#
  static class SuperBIT {
   long[] dataMul, dataAdd;
   SuperBIT(int n) {
     dataMul = new long[n];
     dataAdd = new long[n];
   }
   void update(int left, int right, long val) {
     internalUpdate(left, val, -val * (left-1));
     internalUpdate(right, -val, val * right);
   }
   void internalUpdate(int at, long mul, long add) {
     while (at < dataMul.length) {
       dataMul[at] += mul;
       dataAdd[at] += add;
       at |= (at + 1);
     }
   }
   long query(int at) {
     long mul = 0;
     long add = 0;
     int start = at;
     while(at >= 0) {
       mul += dataMul[at];
       add += dataAdd[at];
       at = (at & (at + 1)) -1;
     }
     return mul * start + add;
   }
   long query(int left, int right) {
     if (left > right) {
       int temp = left;
       left = right;
       right = temp;
     }
     return query(right) - (left > 0 ? query(left-1) : 0);
   }
  }
  //$

}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

/*
Fools and Roads
192 E
http://codeforces.com/problemset/problem/192/E
LCA
 */

/*
5
1 2
1 3
2 4
2 5
2
1 4
3 5

5
3 4
4 5
1 4
2 4
3
2 3
1 3
3 5
 */
public class CF_192E implements Runnable {

  public static void main(String[] args) {
    new Thread(null, new CF_192E(), "", 1 << 25).start();
//    new CF_192E().run();
  }
  public void run() {
    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      PrintWriter out = new PrintWriter(System.out);

      int n = Integer.parseInt(in.readLine());
      HLD g = new HLD(n);
      int[][] edges = new int[2][n];
      for(int i = 0; i < n-1; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        edges[0][i] = v1;
        edges[1][i] = v2;

        g.add(v1, v2);
      }
//      g.goItr(0);
      g.go(0);

      int k = Integer.parseInt(in.readLine());
      for(int i = 0; i < k; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        g.update(v1, v2, 1);
      }

      for(int i = 0; i < n - 1; ++i) {
        out.print(g.count(edges[0][i], edges[1][i]) +" ");
      }
      out.println();

      out.close();
      in.close();
    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  //# Heavy-Light Decomposition
  //Decomposes a tree into heavy and light paths for logn path queries
  //in the tree. Can be used to find the lowest common ancestor as well
  //as to increment counts on nodes along the path between two nodes.
  //Uses: Create the HLD, add all the edges, run go(root). //$
  static class HLD {
   ArrayList<Integer>[] adj;
   int n;
   // hN - nodes sorted by heavy index, stores actual index.
   // hI - the heavy index in the tree.
   // hP - the heavyIndex of the node that starts the heavy path to this node.
   // lP - the heavyIndex of the node that is the parent of the heavyPath node.
   int[] hN, hI, hP, lP, size, par;
   SuperBIT bit;
   HLD(int N) {
     adj = new ArrayList[n = N];
     bit = new SuperBIT(n);
     for(int i = 0; i < n; ++i)
       adj[i] = new ArrayList<Integer>();
     hN = new int[n]; hI = new int[n];
     hP = new int[n]; lP = new int[n];
     size = new int[n];
     par = new int[n];
     Arrays.fill(size, 1);
     Arrays.fill(par, -1);
   }
   void add(int v1, int v2) {
     adj[v1].add(v2); adj[v2].add(v1);
   }

   static boolean getDepth;
   static int ind = 0;
   void go(int start) {
     getDepth = true; dfs(start);
     getDepth = false; dfs(start);
   }
   void dfs(int v) {
     if(!getDepth) {
       int p = par[v];
       hI[v] = ind; hN[ind] = v;
       if(p != -1 && hI[p] == ind-1) {
         hP[v] = hP[p];
         lP[v] = lP[p];
       } else {
         hP[v] = ind;
         if(p != -1) lP[v] = hI[p];
         else lP[v] = ind;
       }
       ++ind;
     }
     int max = 0;
     for(int c = 0; c < adj[v].size(); ++c) {
       int u = adj[v].get(c);
       if(u == par[v]) continue;
       par[u] = v; dfs(u);
       if(getDepth) {
         size[v] += size[u];
         if(size[max] < size[u]) max = c;
       }
     }
     if(max > 0)
       Collections.swap(adj[v], 0, max);
   }

   //#
   void goItr(int start) {
     dfsItr(start, true);
     dfsItr(start, false);
   }
   void dfsItr(int start, boolean getDepth) {
     int[] children = new int[n];
     ArrayDeque<Integer> st = new ArrayDeque<Integer>(n);
     st.add(start);
     int ind = 0;
     boolean cont = false;
     int[][] largestChild = null;
     if(getDepth)
       largestChild = new int[2][n];
     while(!st.isEmpty()) {
       int node = st.pop();
       int p = par[node];
       int child = children[node];
       int before = 0;
       if(child < 0) {
         child = -child -1;
         before = 1;
       }
       if(!getDepth && child == 0 && before == 0) {
         hI[node] = ind;
         hN[ind] = node;
         if(cont) {
           hP[hN[ind]] = hP[hN[ind-1]];
           lP[hN[ind]] = lP[hN[ind-1]];
         } else {
           cont = true;
           hP[hN[ind]] = ind;
           if(p != -1) {
             lP[hN[ind]] = hI[p];
           } else {
             lP[hN[ind]] = ind;
           }
         }
         ++ind;
       }
       if(before == 0)
         if(child < adj[node].size() && p == adj[node].get(child))
           ++child;
       if(child < adj[node].size()) {
         int next = adj[node].get(child);
         if(before == 0) {
           children[node] = -child-1;
           st.push(node);
           par[next] = node;
           st.push(next);
         } else {
           children[node] = 1+child;
           st.push(node);
           if(getDepth) {
             size[node] += size[next];
             if(largestChild[1][node] < size[next]) {
               largestChild[1][node] = size[next];
               largestChild[0][node] = child;
             }
           } else {
             cont = false;
           }
         }
       } else if(!getDepth) {
         cont = false;
       }
     }
     if(getDepth)
       for(int i = 0; i < n; ++i)
         if(largestChild[0][i] > 0)
           Collections.swap(adj[i], 0, largestChild[0][i]);
   }
   //$
   // Lowest common ancestor
   int lca(int v1, int v2) {
     int n1 = hI[v1], n2 = hI[v2];
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]])
         if(n1 < n2) return hN[n1];
         else return hN[n2];
       else if(n1 > n2) n1 = lP[hN[n1]];
       else n2 = lP[hN[n2]];
     }
   }
   // Count node values along the path from v1 to v2.
   long count(int v1, int v2) {
     int n1 = hI[v1], n2 = hI[v2];
     long ans = 0;
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]]) {
         if(n1 > n2) {
           ans += bit.query(n2+1, n1);
         } else if(n1 < n2) {
           ans += bit.query(n1+1, n2);
         }
         break;
       } else if(n1 > n2) {
         ans += bit.query(hP[hN[n1]], n1);
         n1 = lP[hN[n1]];
       } else {
         ans += bit.query(hP[hN[n2]], n2);
         n2 = lP[hN[n2]];
       }
     }
     return ans;
   }
   // Update every node along the path from v1 to v2 by the delta
   void update(int v1, int v2, long delta) {
     int n1 = hI[v1], n2 = hI[v2];
     while(true) {
       if(hP[hN[n1]] == hP[hN[n2]]) {
         if(n1 > n2) {
           bit.update(n2+1, n1, delta);
         } else if(n1 < n2) {
           bit.update(n1+1, n2, delta);
         }
         break;
       } else if(n1 > n2) {
         bit.update(hP[hN[n1]], n1, delta);
         n1 = lP[hN[n1]];
       } else {
         bit.update(hP[hN[n2]], n2, delta);
         n2 = lP[hN[n2]];
       }
     }
   }
  }
  //#
  static class SuperBIT {
   long[] dataMul, dataAdd;
   SuperBIT(int n) {
     dataMul = new long[n];
     dataAdd = new long[n];
   }
   void update(int left, int right, long val) {
     internalUpdate(left, val, -val * (left-1));
     internalUpdate(right, -val, val * right);
   }
   void internalUpdate(int at, long mul, long add) {
     while (at < dataMul.length) {
       dataMul[at] += mul;
       dataAdd[at] += add;
       at |= (at + 1);
     }
   }
   long query(int at) {
     long mul = 0;
     long add = 0;
     int start = at;
     while(at >= 0) {
       mul += dataMul[at];
       add += dataAdd[at];
       at = (at & (at + 1)) -1;
     }
     return mul * start + add;
   }
   long query(int left, int right) {
     if (left > right) {
       int temp = left;
       left = right;
       right = temp;
     }
     return query(right) - (left > 0 ? query(left-1) : 0);
   }
  }
  //$

}


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

/*
Fools and Roads
192 E
http://codeforces.com/problemset/problem/192/E
LCA
 */

/*
5
1 2
1 3
2 4
2 5
2
1 4
3 5

5
3 4
4 5
1 4
2 4
3
2 3
1 3
3 5

 */
public class CF_192E implements Runnable {

  public static void main(String[] args) {
    new Thread(null, new CF_192E(), "", 1 << 25).start();
//    new CF_192E().run();
  }
  public void run() {
    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      PrintWriter out = new PrintWriter(System.out);

      int n = Integer.parseInt(in.readLine());
      HLD g = new HLD(n);
      int[][] edges = new int[2][n];
      for(int i = 0; i < n-1; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        edges[0][i] = v1;
        edges[1][i] = v2;

        g.add(v1, v2);
      }
//      g.goItr(0);
      g.go(0);
//      System.out.println(Arrays.toString(g.v2H));
//      System.out.println(Arrays.toString(g.h2V));
//      System.out.println(Arrays.toString(g.hP));
//      System.out.println(Arrays.toString(g.lP));

      int k = Integer.parseInt(in.readLine());
      for(int i = 0; i < k; ++i) {
        StringTokenizer tok = new StringTokenizer(in.readLine());
        int v1 = Integer.parseInt(tok.nextToken())-1;
        int v2 = Integer.parseInt(tok.nextToken())-1;

        g.update(v1, v2, 1);
      }

      for(int i = 0; i < n - 1; ++i) {
        out.print(g.count(edges[0][i], edges[1][i]) +" ");
      }
      out.println();

      out.close();
      in.close();
    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  //# Heavy-Light Decomposition
  //Decomposes a tree into heavy and light paths for logn path queries
  //in the tree. Can be used to find the lowest common ancestor as well
  //as to increment counts on nodes along the path between two nodes.
  //Uses: Create the HLD, add all the edges, run go(root). //$
  static class HLD {
   ArrayList<Integer>[] adj;
   int n;
   // hN - nodes sorted by heavy index, stores actual index.
   // hI - the heavy index in the tree.
   // hP - the heavyIndex of the node that starts the heavy path to this node.
   // lP - the heavyIndex of the node that is the parent of the heavyPath node.
   int[] h2V, v2H, hP, lP, size, par;
   SuperBIT bit;
   HLD(int N) {
     adj = new ArrayList[n = N];
     bit = new SuperBIT(n);
     for(int i = 0; i < n; ++i)
       adj[i] = new ArrayList<Integer>();
     h2V = new int[n]; v2H = new int[n];
     hP = new int[n]; lP = new int[n];
     size = new int[n];
     par = new int[n];
     Arrays.fill(size, 1);
     Arrays.fill(par, -1);
   }
   void add(int v1, int v2) {
     adj[v1].add(v2); adj[v2].add(v1);
   }

   static boolean getDepth;
   static int ind = 0;
   void go(int start) {
     getDepth = true; dfs(start);
//     getDepth = false; dfs(start);
//     dfs2(start); dfs3(start);

     ArrayDeque<Integer> q = new ArrayDeque<Integer>();
     q.add(start);
     int ind = 0;
     lP[start] = hP[start] = 0;
     while(!q.isEmpty()) {
       int at = q.poll();
//       System.out.println(at +" "+par[at] +" " +(par[at] == -1? -1 : v2H[par[at]]));
       int h = ind;
       if(par[at] != -1) lP[at] = v2H[par[at]];
       int l = lP[at];
       while(true) {
//         System.out.println("hp: "+h +" at: " + at +" ind: "+ind);
         for(int c = adj[at].size()-1; c > 0; --c) {
           int u = adj[at].get(c);
           if(u == par[at]) continue;
           q.push(u);
         }
         lP[at] = l;
         hP[at] = h;
         h2V[ind] = at;
         v2H[at] = ind++;
         if(adj[at].size() <= 1 && at != start) break;
         at = adj[at].get(0);
       }
     }
   }
   void dfs(int v) {
     if(!getDepth) {
       int p = par[v];
       v2H[v] = ind; h2V[ind] = v;
       if(p != -1 && v2H[p] == ind-1) {
         hP[v] = hP[p]; lP[v] = lP[p];
       } else {
         hP[v] = ind;
         if(p != -1) lP[v] = v2H[p];
         else lP[v] = ind;
       }
       ++ind;
     }
     int max = 0, mm = 0, p = -1, c;
     for(c = 0; c < adj[v].size(); ++c) {
       int u = adj[v].get(c);
       if(u == par[v]) { p = c; continue; }
       par[u] = v; dfs(u);
       if(getDepth) {
         int s = size[u]; size[v] += s;
         if(mm < s) { max = c; mm = s; }
       }
     }
//     System.out.println(max +" " +p +" "+adj[v]);
     if(max > 0)
       Collections.swap(adj[v], 0, max);
//     if(p == 0) p = max;
//     if(p > 0)
//       Collections.swap(adj[v], c-1, p);
//     System.out.println("> "+adj[v]);
   }

   void dfs2(int v) {
     int max = 0, p = -1, c;
     for(c = 0; c < adj[v].size(); ++c) {
       int u = adj[v].get(c);
       if(u == par[v]) {p = c; continue;}
       par[u] = v; dfs2(u);
       size[v] += size[u];
       if(size[max] < size[u]) max = c;
     }
     if(max > 0)
       Collections.swap(adj[v], 0, max);
//     if(p == 0) p = max;
//     if(p != -1)
//       Collections.swap(adj[v], c-1, p);
   }
   void dfs3(int v) {
     {
       int p = par[v];
       v2H[v] = ind; h2V[ind] = v;
       if(p != -1 && v2H[p] == ind-1) {
         hP[v] = hP[p]; lP[v] = lP[p];
       } else {
         hP[v] = ind;
         if(p != -1) lP[v] = v2H[p];
         else lP[v] = ind;
       }
       ++ind;
     }
     for(int u : adj[v]) {
       if(u == par[v]) continue; dfs3(u);
     }
   }

   //#
   void goItr(int start) {
     dfsItr(start, true);
     dfsItr(start, false);
   }
   void dfsItr(int start, boolean getDepth) {
     int[] children = new int[n];
     ArrayDeque<Integer> st = new ArrayDeque<Integer>(n);
     st.add(start);
     int ind = 0;
     boolean cont = false;
     int[][] largestChild = null;
     if(getDepth)
       largestChild = new int[2][n];
     while(!st.isEmpty()) {
       int node = st.pop();
       int p = par[node];
       int child = children[node];
       int before = 0;
       if(child < 0) {
         child = -child -1;
         before = 1;
       }
       if(!getDepth && child == 0 && before == 0) {
         v2H[node] = ind;
         h2V[ind] = node;
         if(cont) {
           hP[h2V[ind]] = hP[h2V[ind-1]];
           lP[h2V[ind]] = lP[h2V[ind-1]];
         } else {
           cont = true;
           hP[h2V[ind]] = ind;
           if(p != -1) {
             lP[h2V[ind]] = v2H[p];
           } else {
             lP[h2V[ind]] = ind;
           }
         }
         ++ind;
       }
       if(before == 0)
         if(child < adj[node].size() && p == adj[node].get(child))
           ++child;
       if(child < adj[node].size()) {
         int next = adj[node].get(child);
         if(before == 0) {
           children[node] = -child-1;
           st.push(node);
           par[next] = node;
           st.push(next);
         } else {
           children[node] = 1+child;
           st.push(node);
           if(getDepth) {
             size[node] += size[next];
             if(largestChild[1][node] < size[next]) {
               largestChild[1][node] = size[next];
               largestChild[0][node] = child;
             }
           } else {
             cont = false;
           }
         }
       } else if(!getDepth) {
         cont = false;
       }
     }
     if(getDepth)
       for(int i = 0; i < n; ++i)
         if(largestChild[0][i] > 0)
           Collections.swap(adj[i], 0, largestChild[0][i]);
   }
   //$
   // Lowest common ancestor
   int lca(int v1, int v2) {
     int n1 = v2H[v1], n2 = v2H[v2];
     while(true) {
       if(hP[h2V[n1]] == hP[h2V[n2]])
         if(n1 < n2) return h2V[n1];
         else return h2V[n2];
       else if(n1 > n2) n1 = lP[h2V[n1]];
       else n2 = lP[h2V[n2]];
     }
   }
   // Count node values along the path from v1 to v2.
   long count(int v1, int v2) {
     int n1 = v2H[v1], n2 = v2H[v2];
     long ans = 0;
     while(true) {
       if(hP[h2V[n1]] == hP[h2V[n2]]) {
         if(n1 > n2) {
           ans += bit.query(n2+1, n1);
         } else if(n1 < n2) {
           ans += bit.query(n1+1, n2);
         }
         break;
       } else if(n1 > n2) {
         ans += bit.query(hP[h2V[n1]], n1);
         n1 = lP[h2V[n1]];
       } else {
         ans += bit.query(hP[h2V[n2]], n2);
         n2 = lP[h2V[n2]];
       }
     }
     return ans;
   }
   // Update every node along the path from v1 to v2 by the delta
   void update(int v1, int v2, long delta) {
     int n1 = v2H[v1], n2 = v2H[v2];
     while(true) {
       if(hP[h2V[n1]] == hP[h2V[n2]]) {
         if(n1 > n2) {
           bit.update(n2+1, n1, delta);
         } else if(n1 < n2) {
           bit.update(n1+1, n2, delta);
         }
         break;
       } else if(n1 > n2) {
         bit.update(hP[h2V[n1]], n1, delta);
         n1 = lP[h2V[n1]];
       } else {
         bit.update(hP[h2V[n2]], n2, delta);
         n2 = lP[h2V[n2]];
       }
     }
   }
  }
  //#
  static class SuperBIT {
   long[] dataMul, dataAdd;
   SuperBIT(int n) {
     dataMul = new long[n];
     dataAdd = new long[n];
   }
   void update(int left, int right, long val) {
     internalUpdate(left, val, -val * (left-1));
     internalUpdate(right, -val, val * right);
   }
   void internalUpdate(int at, long mul, long add) {
     while (at < dataMul.length) {
       dataMul[at] += mul;
       dataAdd[at] += add;
       at |= (at + 1);
     }
   }
   long query(int at) {
     long mul = 0;
     long add = 0;
     int start = at;
     while(at >= 0) {
       mul += dataMul[at];
       add += dataAdd[at];
       at = (at & (at + 1)) -1;
     }
     return mul * start + add;
   }
   long query(int left, int right) {
     if (left > right) {
       int temp = left;
       left = right;
       right = temp;
     }
     return query(right) - (left > 0 ? query(left-1) : 0);
   }
  }
  //$

}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.StringTokenizer;

public class FoolsAndRoads {

	static class SparseTable {
		int[][] spT;

		SparseTable(int n) {
			int k = (int) Math.floor(Math.log(n) / Math.log(2)) + 1;
			spT = new int[n][k];
			for (int i = 0; i < n; i++)
				spT[i][0] = i;
			for (int j = 1; 1 << j <= n; j++)
				for (int i = 0; i + (1 << j) - 1 < n; i++)
					if (level[spT[i][j - 1]] < level[spT[i + (1 << (j - 1))][j - 1]])
						spT[i][j] = spT[i][j - 1];
					else
						spT[i][j] = spT[i + (1 << (j - 1))][j - 1];
		}

		int query(int i, int j) {
			int k = (int) Math.floor(Math.log(j - i + 1) / Math.log(2));
			if (spT[i][k] < spT[j - (1 << k) + 1][k])
				return spT[i][k];
			else
				return spT[j - (1 << k) + 1][k];
		}
	}

	static class Edge {
		int node, idx;

		Edge(int a, int b) {
			node = a;
			idx = b;
		}

		@Override
		public String toString() {
			return node + " " + idx;
		}
	}

	static ArrayList<Edge>[] graph;
	static int[] euler, level, index, ans, from, thro;
	static int idx;

	static void dfs(int u, int p, int d) {
		index[u] = idx;
		level[idx] = d;
		euler[idx++] = u;
		for (Edge v : graph[u]) {
			if (v.node != p) {
				dfs(v.node, u, d + 1);
				euler[idx] = u;
				level[idx++] = d;
			}
		}
	}

	static int dfs2(int u, int p) {
		int now = from[u];
		for (Edge e : graph[u]) {
			if (p != e.node) {
				int res = dfs2(e.node, u);
				ans[e.idx] = res;
				now += res;
			}
		}
		return now - thro[u];
	}

	public static void main(String[] args) throws IOException {
		MyScanner sc = new MyScanner(System.in);
		PrintWriter out = new PrintWriter(System.out);
		int n = sc.nextInt();
		graph = new ArrayList[n];
		for (int i = 0; i < n; i++)
			graph[i] = new ArrayList<>();
		for (int i = 0; i < n - 1; i++) {
			int u = sc.nextInt() - 1, v = sc.nextInt() - 1;
			graph[u].add(new Edge(v, i));
			graph[v].add(new Edge(u, i));
		}
		euler = new int[n << 1];
		level = new int[n << 1];
		index = new int[n];
		dfs(0, -1, 0);
		SparseTable spT = new SparseTable(n << 1);
		ans = new int[n];
		from = new int[n];
		thro = new int[n];
		int m = sc.nextInt();
		while (m-- > 0) {
			int u = sc.nextInt() - 1, v = sc.nextInt() - 1;
			if (index[u] > index[v]) {
				u ^= v;
				v ^= u;
				u ^= v;
			}
			int lca = euler[spT.query(index[u], index[v])];
			from[u]++;
			from[v]++;
			thro[lca] += 2;
		}
		dfs2(0, -1);
		for (int i = 0; i < n - 1; i++) {
			out.print(ans[i]);
			if (i != n - 1)
				out.print(" ");
		}
		out.close();
	}

	static class MyScanner {

		BufferedReader br;
		StringTokenizer st;

		MyScanner(InputStream is) {
			br = new BufferedReader(new InputStreamReader(is));
		}

		String next() throws IOException {
			while (st == null || !st.hasMoreTokens())
				st = new StringTokenizer(br.readLine());
			return st.nextToken();
		}

		int nextInt() throws IOException {
			return Integer.parseInt(next());
		}

		long nextLong() throws IOException {
			return Long.parseLong(next());
		}
	}
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.StringTokenizer;

public class FoolsAndRoads {

	static class SparseTable {
		int[][] spT;

		SparseTable(int n) {
			int k = (int) Math.floor(Math.log(n) / Math.log(2)) + 1;
			spT = new int[n][k];
			for (int i = 0; i < n; i++)
				spT[i][0] = i;
			for (int j = 1; 1 << j <= n; j++)
				for (int i = 0; i + (1 << j) - 1 < n; i++)
					if (level[spT[i][j - 1]] < level[spT[i + (1 << (j - 1))][j - 1]])
						spT[i][j] = spT[i][j - 1];
					else
						spT[i][j] = spT[i + (1 << (j - 1))][j - 1];
		}

		int query(int i, int j) {
			int k = (int) Math.floor(Math.log(j - i + 1) / Math.log(2));
			if (spT[i][k] <= spT[j - (1 << k) + 1][k])
				return spT[i][k];
			else
				return spT[j - (1 << k) + 1][k];
		}
	}

	static class Edge {
		int node, idx;

		Edge(int a, int b) {
			node = a;
			idx = b;
		}

		@Override
		public String toString() {
			return node + " " + idx;
		}
	}

	static ArrayList<Edge>[] graph;
	static int[] euler, level, index, ans, from, thro;
	static int idx;

	static void dfs(int u, int p, int d) {
		index[u] = idx;
		level[idx] = d;
		euler[idx++] = u;
		for (Edge v : graph[u]) {
			if (v.node != p) {
				dfs(v.node, u, d + 1);
				euler[idx] = u;
				level[idx++] = d;
			}
		}
	}

	static int dfs2(int u, int p) {
		int now = from[u];
		for (Edge e : graph[u]) {
			if (p != e.node) {
				int res = dfs2(e.node, u);
				ans[e.idx] = res;
				now += res;
			}
		}
		return now - thro[u];
	}

	public static void main(String[] args) throws IOException {
		MyScanner sc = new MyScanner(System.in);
		PrintWriter out = new PrintWriter(System.out);
		int n = sc.nextInt();
		graph = new ArrayList[n];
		for (int i = 0; i < n; i++)
			graph[i] = new ArrayList<>();
		for (int i = 0; i < n - 1; i++) {
			int u = sc.nextInt() - 1, v = sc.nextInt() - 1;
			graph[u].add(new Edge(v, i));
			graph[v].add(new Edge(u, i));
		}
		euler = new int[n << 1];
		level = new int[n << 1];
		index = new int[n];
		idx = 0;
		dfs(0, -1, 0);
		SparseTable spT = new SparseTable(n << 1);
		ans = new int[n];
		from = new int[n];
		thro = new int[n];
		int m = sc.nextInt();
		while (m-- > 0) {
			int u = sc.nextInt() - 1, v = sc.nextInt() - 1;
			from[u]++;
			from[v]++;
			if (index[u] > index[v]) {
				u ^= v;
				v ^= u;
				u ^= v;
			}
			int lca = euler[spT.query(index[u], index[v])];
			thro[lca] += 2;
		}
		dfs2(0, -1);
		for (int i = 0; i < n - 1; i++) {
			out.print(ans[i]);
			if (i != n - 1)
				out.print(" ");
		}
		out.close();
	}

	static class MyScanner {

		BufferedReader br;
		StringTokenizer st;

		MyScanner(InputStream is) {
			br = new BufferedReader(new InputStreamReader(is));
		}

		String next() throws IOException {
			while (st == null || !st.hasMoreTokens())
				st = new StringTokenizer(br.readLine());
			return st.nextToken();
		}

		int nextInt() throws IOException {
			return Integer.parseInt(next());
		}

		long nextLong() throws IOException {
			return Long.parseLong(next());
		}
	}
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.StringTokenizer;

public class FoolsAndRoads {

	static class SparseTable {
		int[][] spT;

		SparseTable(int n) {
			int k = (int) Math.floor(Math.log(n) / Math.log(2)) + 1;
			spT = new int[n][k];
			for (int i = 0; i < n; i++)
				spT[i][0] = i;
			for (int j = 1; 1 << j <= n; j++)
				for (int i = 0; i + (1 << j) - 1 < n; i++)
					if (level[spT[i][j - 1]] < level[spT[i + (1 << (j - 1))][j - 1]])
						spT[i][j] = spT[i][j - 1];
					else
						spT[i][j] = spT[i + (1 << (j - 1))][j - 1];
		}

		int query(int i, int j) {
			int k = (int) Math.floor(Math.log(j - i + 1) / Math.log(2));
			if (level[spT[i][k]] <= level[spT[j - (1 << k) + 1][k]])
				return spT[i][k];
			else
				return spT[j - (1 << k) + 1][k];
		}
	}

	static class Edge {
		int node, idx;

		Edge(int a, int b) {
			node = a;
			idx = b;
		}

		@Override
		public String toString() {
			return node + " " + idx;
		}
	}

	static ArrayList<Edge>[] graph;
	static int[] euler, level, index, ans, from, thro;
	static int idx;

	static void dfs(int u, int p, int d) {
		index[u] = idx;
		level[idx] = d;
		euler[idx++] = u;
		for (Edge v : graph[u]) {
			if (v.node != p) {
				dfs(v.node, u, d + 1);
				euler[idx] = u;
				level[idx++] = d;
			}
		}
	}

	static int dfs2(int u, int p) {
		int now = from[u];
		for (Edge e : graph[u]) {
			if (p != e.node) {
				int res = dfs2(e.node, u);
				ans[e.idx] = res;
				now += res;
			}
		}
		return now - thro[u];
	}

	public static void main(String[] args) throws IOException {
		MyScanner sc = new MyScanner(System.in);
		PrintWriter out = new PrintWriter(System.out);
		int n = sc.nextInt();
		graph = new ArrayList[n];
		for (int i = 0; i < n; i++)
			graph[i] = new ArrayList<>();
		for (int i = 0; i < n - 1; i++) {
			int u = sc.nextInt() - 1, v = sc.nextInt() - 1;
			graph[u].add(new Edge(v, i));
			graph[v].add(new Edge(u, i));
		}
		euler = new int[n << 1];
		level = new int[n << 1];
		index = new int[n];
		idx = 0;
		dfs(0, -1, 0);
		SparseTable spT = new SparseTable(n << 1);
		ans = new int[n];
		from = new int[n];
		thro = new int[n];
		int m = sc.nextInt();
		while (m-- > 0) {
			int u = sc.nextInt() - 1, v = sc.nextInt() - 1;
			from[u]++;
			from[v]++;
			if (index[u] > index[v]) {
				u ^= v;
				v ^= u;
				u ^= v;
			}
			int lca = euler[spT.query(index[u], index[v])];
			thro[lca] += 2;
		}
		dfs2(0, -1);
		for (int i = 0; i < n - 1; i++) {
			out.print(ans[i]);
			if (i != n - 1)
				out.print(" ");
		}
		out.close();
	}

	static class MyScanner {

		BufferedReader br;
		StringTokenizer st;

		MyScanner(InputStream is) {
			br = new BufferedReader(new InputStreamReader(is));
		}

		String next() throws IOException {
			while (st == null || !st.hasMoreTokens())
				st = new StringTokenizer(br.readLine());
			return st.nextToken();
		}

		int nextInt() throws IOException {
			return Integer.parseInt(next());
		}

		long nextLong() throws IOException {
			return Long.parseLong(next());
		}
	}
}