#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9 + 5;
struct segment_change {
  static const int SENTINEL = numeric_limits<int>::min();
  int to_set;
  segment_change(int _to_set = INF) : to_set(_to_set) {}
  bool has_set() const { return to_set != INF; }
  void reset() { to_set = INF; }
  bool has_change() const { return has_set(); }
  segment_change combine(const segment_change &other) const { return other; }
};
struct segment {
  int minimum;
  segment(int _minimum = INF) : minimum(_minimum) {}
  void apply(int, const segment_change &change) {
    if (change.has_set()) minimum = change.to_set;
  }
  void join(const segment &other) { minimum = min(minimum, other.minimum); }
  void join(const segment &a, const segment &b) {
    *this = a;
    join(b);
  }
};
struct persistent_seg_tree {
  static const bool LAZY_PROPAGATION = false;
  struct node {
    segment seg;
    segment_change change;
    int left = -1, right = -1;
  };
  int tree_n = 0;
  vector<node> tree;
  persistent_seg_tree(int n = 0, int reserve_size = 0) {
    if (n > 0) init(n, reserve_size);
  }
  void init(int n, int reserve_size = 0) {
    tree_n = 1;
    while (tree_n < n) tree_n *= 2;
    if (reserve_size > 0) tree.reserve(reserve_size);
    tree.assign(2 * tree_n, node());
    for (int i = 1; i < tree_n; i++) {
      tree[i].left = 2 * i;
      tree[i].right = 2 * i + 1;
    }
  }
  int &left(int position) { return tree[position].left; }
  int &right(int position) { return tree[position].right; }
  segment &seg(int position) { return tree[position].seg; }
  segment_change &change(int position) { return tree[position].change; }
  void build(const vector<segment> &initial) {
    int n = initial.size();
    assert(n <= tree_n);
    for (int i = 0; i < n; i++) seg(tree_n + i) = initial[i];
    for (int position = tree_n - 1; position > 0; position--)
      seg(position).join(seg(left(position)), seg(right(position)));
  }
  int make_copy(int position) {
    assert(0 <= position && position < (int)tree.size());
    tree.push_back(tree[position]);
    return (int)tree.size() - 1;
  }
  void push_down(int position, int length) {
    if (!change(position).has_change()) return;
    seg(left(position)).apply(length / 2, change(position));
    seg(right(position)).apply(length / 2, change(position));
    change(left(position)) = change(left(position)).combine(change(position));
    change(right(position)) = change(right(position)).combine(change(position));
    change(position).reset();
  }
  void query_tree(int position, int start, int end, int a, int b,
                  segment &answer, const segment_change &propagate) {
    if (a <= start && end <= b) {
      segment current = seg(position);
      current.apply(end - start, propagate);
      answer.join(current);
      return;
    }
    if (left(position) == -1 || right(position) == -1) return;
    int mid = (start + end) / 2;
    segment_change next_propagate = change(position).combine(propagate);
    if (a < mid)
      query_tree(left(position), start, mid, a, b, answer, next_propagate);
    if (b > mid)
      query_tree(right(position), mid, end, a, b, answer, next_propagate);
  }
  segment query(int root, int a, int b) {
    assert(0 <= a && a <= b && b <= tree_n);
    segment answer;
    query_tree(root, 0, tree_n, a, b, answer, segment_change());
    return answer;
  }
  void set_left(int position, int result) { left(position) = result; }
  void set_right(int position, int result) { right(position) = result; }
  int update_tree(int position, int start, int end, int a, int b,
                  const segment_change &request, bool needs_copy) {
    if (needs_copy) position = make_copy(position);
    if (a <= start && end <= b) {
      seg(position).apply(end - start, request);
      change(position) = change(position).combine(request);
      return position;
    }
    if (left(position) == -1 || right(position) == -1) return position;
    int mid = (start + end) / 2;
    if (LAZY_PROPAGATION) {
      set_left(position, make_copy(left(position)));
      set_right(position, make_copy(right(position)));
      push_down(position, end - start);
      needs_copy = false;
    }
    if (a < mid)
      set_left(position, update_tree(left(position), start, mid, a, b, request,
                                     needs_copy));
    if (b > mid)
      set_right(position, update_tree(right(position), mid, end, a, b, request,
                                      needs_copy));
    seg(position).join(seg(left(position)), seg(right(position)));
    return position;
  }
  int update(int root, int a, int b, const segment_change &change) {
    assert(0 <= a && a <= b && b <= tree_n);
    assert(LAZY_PROPAGATION || b - a <= 1);
    return update_tree(root, 0, tree_n, a, b, change, true);
  }
};
int N;
vector<int> A;
vector<int> matching;
vector<int> jumps;
vector<int> roots;
void solve_case() {
  cin >> N;
  A.resize(N);
  for (int &a : A) {
    cin >> a;
    a--;
  }
  matching.assign(N, N);
  jumps.assign(N + 1, 0);
  persistent_seg_tree tree(N);
  roots.assign(N + 1, 1);
  long long answer = 0;
  for (int i = N - 1; i >= 0; i--) {
    int location = tree.query(roots[i + 1], A[i], A[i] + 1).minimum;
    if (location < N) {
      assert(A[i] == A[location]);
      matching[i] = location;
      jumps[i] = jumps[location + 1] + 1;
    }
    answer += jumps[i];
    location = min(location + 1, N);
    roots[i] = tree.update(roots[location], A[i], A[i] + 1, segment_change(i));
  }
  cout << answer << '\n';
}
int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int Q;
  cin >> Q;
  while (Q-- > 0) solve_case();
}
