#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-10;
const int inf = 0x3f3f3f3f, N = 1e5 + 10;
const double pi = acos(-1);
using namespace std;
struct Node {
  double r, h;
} p[N];
double V(Node a) { return a.r * a.r * a.h * pi; }
double dp[N], now[N], v[N], f[N];
int num[N];
struct node {
  int l, r;
  double maxn;
} s[100010 * 4];
int n, m;
void pushup(int cnt) {
  s[cnt].maxn = max(s[cnt << 1].maxn, s[cnt << 1 | 1].maxn);
}
void build_tree(int l, int r, int cnt) {
  s[cnt].l = l;
  s[cnt].r = r;
  if (l == r) {
    s[cnt].maxn = 0.0;
    return;
  }
  int mid = (l + r) >> 1;
  build_tree(l, mid, cnt * 2);
  build_tree(mid + 1, r, cnt * 2 + 1);
  pushup(cnt);
}
void update(int x, double numx, int cnt) {
  if (s[cnt].l == x && s[cnt].r == x) {
    s[cnt].maxn = max(s[cnt].maxn, numx);
    return;
  }
  int mid = (s[cnt].l + s[cnt].r) >> 1;
  if (x <= mid) update(x, numx, cnt * 2);
  if (x > mid) update(x, numx, cnt * 2 + 1);
  pushup(cnt);
}
double query(int l, int r, int cnt) {
  if (s[cnt].l >= l && s[cnt].r <= r) return s[cnt].maxn;
  int mid = (s[cnt].l + s[cnt].r) >> 1;
  double res = 0.0;
  if (l <= mid) res = max(query(l, r, cnt * 2), res);
  if (r > mid) res = max(query(l, r, cnt * 2 + 1), res);
  return res;
}
int main() {
  int n;
  while (cin >> n) {
    build_tree(1, n, 1);
    memset(dp, 0, sizeof(dp));
    for (int i = 0; i < n; i++) {
      scanf("%lf%lf", &p[i].r, &p[i].h);
      v[i] = V(p[i]);
      f[i] = v[i];
    }
    sort(f, f + n);
    for (int i = 0; i < n; i++) num[i] = lower_bound(f, f + n, v[i]) - f + 1;
    for (int i = 0; i < n; i++) {
      if (num[i] - 1 == 0)
        dp[i] = v[i];
      else
        dp[i] = query(1, num[i] - 1, 1) + v[i];
      update(num[i], dp[i], 1);
    }
    double ans = 0.0;
    for (int i = 0; i < n; i++) ans = max(dp[i], ans);
    printf("%.10f\n", ans);
  }
  return 0;
}
