#define lk k << 1
#define rk k << 1 | 1
#define mk (l + r) >> 1
const int maxn = 1e5 + 10;
struct node {//权值线段树
int num, sum;
}tree[maxn << 2];
int id[maxn], cnt;
bool vis[maxn];
priority_queue< int, vector<int>, greater<int> >q[maxn];//升序,小顶堆
bool cmp(int x, int y) {
return q[x].size() > q[y].size();
}
void push(int k) {
tree[k].num = tree[lk].num + tree[rk].num;
tree[k].sum = tree[lk].sum + tree[rk].sum;
}
void updata(int k, int l, int r, int y, int w) {
if (l == r) {
tree[k].num += w;
tree[k].sum += w * y;
return;
}
int mid = mk;
if (mid >= y) updata(lk, l, mid, y, w);
else updata(rk, mid + 1, r, y, w);
push(k);
}
int query(int k, int l, int r, int len) {//查询区间第len大的数,求和[1, len]
if (len <= 0) return 0;
if (l == r) return len * l;
int ans = 0;
int mid = mk;
if (tree[lk].num <= len) {
ans += tree[lk].sum;
ans += query(rk, mid + 1, r, len - tree[lk].num);
}
else ans += query(lk, l, mid, len);
return ans;
}
int main() {
int n, start = 0, t1, t2;
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%d %d", &t1, &t2);
if (!t1) {
start++;//start 是我一开始就有的选票
continue;
}
q[t1].push(t2);
if (!vis[t1]) {
id[cnt++] = t1;
vis[t1] = 1;
}
updata(1, 0, 10000, t2, 1);
}
sort(id, id + cnt, cmp);
int ans = 1e10, res = 0, num = 0;
int limit = max(start, 1);
for (int i = n; i >= limit; --i) {//这个i是 枚举假设我i张选票时获胜,其他人的票要小于i
for (int j = 0; j < cnt; ++j) {//所以先令所有其他候选人的票小于i,然后我的选票不足i的话再从所以票中拿小的
int temp = id[j];
int sz = q[temp].size();
if (sz < i) break;
while (sz >= i) {
sz--;
int now = q[temp].top();
q[temp].pop();
updata(1, 0, 10000, now, -1);
res += now;
num++;
}
}
ans = min(ans, res + query(1, 0, 10000, i - num - start));
}
printf("%d\n", ans);
return 0;
}