#include <bits/stdc++.h>
template <typename T>
T max(T a, T b) {
  return a > b ? a : b;
}
template <typename T>
T min(T a, T b) {
  return a > b ? b : a;
}
template <typename T>
void Swap(T &a, T &b) {
  T c = a;
  a = b;
  b = c;
}
template <typename T>
inline void read(T &res) {
  res = 0;
  T opt = 1;
  char c = getchar();
  while (!isdigit(c)) {
    if (c == '-') opt = -1;
    c = getchar();
  }
  while (isdigit(c)) {
    res = (res << 1) + (res << 3) + (c - '0');
    c = getchar();
  }
  res *= opt;
}
long long res;
struct Point {
  int x, y;
  bool operator<(const Point pt) const {
    return (x == pt.x and y < pt.y) or x < pt.x;
  }
  bool operator==(const Point pt) const { return x == pt.x and y == pt.y; }
};
class UnionFindSet {
 public:
  int top, stack[300010];
  int root[300010 << 1], rank[300010 << 1], sizex[300010 << 1],
      sizey[300010 << 1];
  void init(int size) {
    for (int i = 0; i <= size; ++i) {
      root[i] = i, rank[i] = 1;
      if (i <= size / 2)
        sizex[i] = 1;
      else
        sizey[i] = 1;
    }
  }
  int setRoot(int x) { return root[x] == x ? x : setRoot(root[x]); }
  void merge(int x, int y) {
    int fx = setRoot(x);
    int fy = setRoot(y);
    if (fx != fy) {
      if (rank[fx] > rank[fy]) {
        Swap(fx, fy);
        Swap(x, y);
      }
      res -= 1ll * sizex[fx] * sizey[fx];
      res -= 1ll * sizex[fy] * sizey[fy];
      sizex[fy] += sizex[fx];
      sizey[fy] += sizey[fx];
      root[fx] = fy;
      rank[fy] += rank[fx];
      res += 1ll * sizex[fy] * sizey[fy];
      stack[++top] = fx;
    }
  }
  void cancle(int currentTop) {
    int fx, fy;
    while (top != currentTop) {
      fx = stack[top];
      fy = root[fx];
      res -= 1ll * sizex[fy] * sizey[fy];
      sizex[fy] -= sizex[fx];
      sizey[fy] -= sizey[fx];
      res += 1ll * sizex[fx] * sizey[fx];
      res += 1ll * sizex[fy] * sizey[fy];
      rank[fy] -= rank[fx];
      root[fx] = fx;
      --top;
    }
  }
};
int upper;
UnionFindSet ufset;
Point pt[300010];
class SegmentTree {
 public:
  struct {
    std::vector<int> idList;
  } node[300010 << 2];
  void insert(int i, int L, int R, int l, int r, int id) {
    if (l > r) return;
    if (l <= L and R <= r) {
      node[i].idList.push_back(id);
    } else {
      int mid = (L + R) >> 1;
      if (l <= mid) insert((i << 1), L, mid, l, r, id);
      if (r > mid) insert((i << 1 | 1), mid + 1, R, l, r, id);
    }
  }
  void solve(int i, int L, int R) {
    int cu = ufset.top;
    for (auto id : node[i].idList) {
      ufset.merge(pt[id].x, pt[id].y + upper);
    }
    if (L == R) {
      printf("%lld ", res);
    } else {
      int mid = (L + R) >> 1;
      solve((i << 1), L, mid);
      solve((i << 1 | 1), mid + 1, R);
    }
    ufset.cancle(cu);
  }
};
int q, tot, appear[300010], disappear[300010];
std::map<Point, int> hash;
SegmentTree segtree;
int main() {
  read(q);
  Point temp;
  int tagx = -1, tagy = -1;
  for (int i = 1; i <= q; ++i) {
    read(temp.x);
    read(temp.y);
    tagx = max(temp.x, tagx);
    tagy = max(temp.y, tagy);
    if (hash[temp]) {
      disappear[hash[temp]] = i - 1;
      hash[temp] = 0;
    } else {
      hash[temp] = ++tot;
      pt[tot] = temp;
      appear[tot] = i;
    }
  }
  upper = max(tagx, tagy);
  ufset.init(2 * upper);
  for (int i = 1; i <= tot; ++i) {
    if (!disappear[i]) disappear[i] = q;
    segtree.insert(1, 1, q, appear[i], disappear[i], i);
  }
  segtree.solve(1, 1, q);
  printf("\n");
  return 0;
}
