#include<bits/stdc++.h>
#define MOD 1000000007
#define ll long long
#define mp make_pair
#define pb push_back
#define N 200010
using namespace std;
int tree[4 * N];
map<int, int> bts;
void update(int low, int high, int ind, int value, int node){
	if(low == high){
		tree[node] = value;
		return;
	}
	int mid = (low + high) / 2;
	if(ind <= mid)
		update(low, mid, ind, value, 2 * node);
	else update(mid + 1, high, ind, value, 2 * node + 1);
	tree[node] = tree[2 * node] + tree[2 * node + 1];
}
int query(int low, int high, int begin, int end, int node){
	if(low > end || high < begin || begin > end)
		return 0;
	if(low >= begin && high <= end)
		return tree[node];
	int mid = (low + high) / 2;
	return query(low, mid, begin, end, 2 * node) + query(mid + 1, high, begin, end, 2 * node + 1);
}
map<int, vector<int> > df;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	int n;
	cin >> n;
	vector<int> rt;
	for(int i = 0; i < n; i++){
		int x, y;
		cin >> x >> y;
		rt.pb(x);
		df[y].pb(x);
	}
	sort(rt.begin(), rt.end());
	for(int i = 0; i < rt.size(); i++){
		bts[rt[i]] = lower_bound(rt.begin(), rt.end(), rt[i]) - rt.begin();
	}
	ll ans = 0;
	set<int> occ;
	for(auto it = df.rbegin(); it != df.rend(); it++){
		set<int> ry;
		vector<int> cur = it->second;
		if(cur.size() == 1){
			occ.insert(cur[0]);
			update(0, n - 1, bts[cur[0]], 1, 1);
			ll x = query(0, n - 1, 0, bts[cur[0]], 1);
			ll y = query(0, n - 1, bts[cur[0]], n - 1, 1);
			ans += x * y;
			continue;
		}
		for(int i = 0; i < cur.size(); i++){
			update(0, n - 1, bts[cur[i]], 1, 1);
		}
		ll curv = 1;
		int mk = 0;
		for(int i = 0; i < cur.size(); i++){
			occ.insert(cur[i]);
			if(i == 0){
				//ll x = query(0, n - 1, bts[cur[i]] + 1, bts[cur[i + 1]] - 1, 1);
				ll y = query(0, n - 1, 0, bts[cur[i]] - 1, 1);
				if(y){
					mk++;
					curv *= (y * (y + 1) / 2);
				}
			}
			else if(i == cur.size() - 1){
				ll x = query(0, n - 1, bts[cur[i - 1]] + 1, bts[cur[i]] - 1, 1);
				ll y = query(0, n - 1, bts[cur[i]] + 1, n - 1, 1);
				if(x && y){
					mk++;
					curv *= (x * (x + 1) / 2) * (y * (y + 1) / 2);
				}
				else if(x){
					mk++;
					curv *= (x * (x + 1) / 2);
				}
				else if(y){
					mk++;
					curv *= (y * (y + 1) / 2);
				}
			}
			else{
				//ll x = query(0, n - 1, bts[cur[i - 1]] + 1, bts[cur[i]], 1);
				ll y = query(0, n - 1, bts[cur[i]] + 1, bts[cur[i + 1]] - 1, 1);
				if(y){
					mk++;
					curv *= (y * (y + 1)) / 2;;
				}
			}
		}
		//cerr << curv << '\n';
		//cerr << ans << '\n';
		if(!mk)curv = 0;
		ans += occ.size() * (occ.size() + 1) / 2 - curv;

	}
	cout << ans << '\n';
}


#include<bits/stdc++.h>
#define MOD 1000000007
#define ll long long
#define mp make_pair
#define pb push_back
#define N 200010
using namespace std;
int tree[4 * N];
map<int, int> bts;
void update(int low, int high, int ind, int value, int node){
	if(low == high){
		tree[node] = value;
		return;
	}
	int mid = (low + high) / 2;
	if(ind <= mid)
		update(low, mid, ind, value, 2 * node);
	else update(mid + 1, high, ind, value, 2 * node + 1);
	tree[node] = tree[2 * node] + tree[2 * node + 1];
}
int query(int low, int high, int begin, int end, int node){
	if(low > end || high < begin || begin > end)
		return 0;
	if(low >= begin && high <= end)
		return tree[node];
	int mid = (low + high) / 2;
	return query(low, mid, begin, end, 2 * node) + query(mid + 1, high, begin, end, 2 * node + 1);
}
map<int, vector<int> > df;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	int n;
	cin >> n;
	vector<int> rt;
	for(int i = 0; i < n; i++){
		int x, y;
		cin >> x >> y;
		rt.pb(x);
		df[y].pb(x);
	}
	sort(rt.begin(), rt.end());
	for(int i = 0; i < rt.size(); i++){
		bts[rt[i]] = lower_bound(rt.begin(), rt.end(), rt[i]) - rt.begin();
	}
	ll ans = 0;
	set<int> occ;
	for(auto it = df.rbegin(); it != df.rend(); it++){
		set<int> ry;
		vector<int> cur = it->second;
		if(cur.size() == 1){
			occ.insert(cur[0]);
			update(0, n - 1, bts[cur[0]], 1, 1);
			ll x = query(0, n - 1, 0, bts[cur[0]], 1);
			ll y = query(0, n - 1, bts[cur[0]], n - 1, 1);
			ans += x * y;
			continue;
		}
		for(int i = 0; i < cur.size(); i++){
			update(0, n - 1, bts[cur[i]], 1, 1);
		}
		ll curv = 1;
		for(int i = 0; i < cur.size(); i++){
			occ.insert(cur[i]);
			if(i == 0){
				//ll x = query(0, n - 1, bts[cur[i]] + 1, bts[cur[i + 1]] - 1, 1);
				ll y = query(0, n - 1, 0, bts[cur[i]] - 1, 1);
				if(y)
			    	curv *= (y * (y + 1) / 2);
			}
			else if(i == cur.size() - 1){
				ll x = query(0, n - 1, bts[cur[i - 1]] + 1, bts[cur[i]] - 1, 1);
				ll y = query(0, n - 1, bts[cur[i]] + 1, n - 1, 1);
				if(x && y)
					curv *= (x * (x + 1) / 2) * (y * (y + 1) / 2);
				else if(x)
					curv *= (x * (x + 1) / 2);
				else if(y)
					curv *= (y * (y + 1) / 2);
			}
			else{
				//ll x = query(0, n - 1, bts[cur[i - 1]] + 1, bts[cur[i]], 1);
				ll y = query(0, n - 1, bts[cur[i]] + 1, bts[cur[i + 1]] - 1, 1);
				if(y)
					curv *= (y * (y + 1)) / 2;;
			}
		}
		//cerr << curv << '\n';
		//cerr << ans << '\n';
		ans += occ.size() * (occ.size() + 1) / 2 - curv;

	}
	cout << ans << '\n';
}


#include<bits/stdc++.h>
#define MOD 1000000007
#define ll long long
#define mp make_pair
#define pb push_back
#define N 200010
using namespace std;
int tree[4 * N];
map<int, int> bts;
void update(int low, int high, int ind, int value, int node){
	if(low == high){
		tree[node] = value;
		return;
	}
	int mid = (low + high) / 2;
	if(ind <= mid)
		update(low, mid, ind, value, 2 * node);
	else update(mid + 1, high, ind, value, 2 * node + 1);
	tree[node] = tree[2 * node] + tree[2 * node + 1];
}
int query(int low, int high, int begin, int end, int node){
	if(low > end || high < begin || begin > end)
		return 0;
	if(low >= begin && high <= end)
		return tree[node];
	int mid = (low + high) / 2;
	return query(low, mid, begin, end, 2 * node) + query(mid + 1, high, begin, end, 2 * node + 1);
}
map<int, set<int> > df;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	int n;
	cin >> n;
	vector<int> rt;
	for(int i = 0; i < n; i++){
		int x, y;
		cin >> x >> y;
		rt.pb(x);
		df[y].insert(x);
	}
	sort(rt.begin(), rt.end());
	for(int i = 0; i < rt.size(); i++){
		bts[rt[i]] = lower_bound(rt.begin(), rt.end(), rt[i]) - rt.begin();
	}
	ll ans = 0;
	set<int> occ;
	for(auto it = df.rbegin(); it != df.rend(); it++){
		//cerr << ans << '\n';
		set<int> rx = it->second;
		vector<int> cur;// = it->second;
		for(auto i = rx.begin(); i != rx.end(); i++)
			cur.pb(*i);

		if(cur.size() == 1){
			occ.insert(cur[0]);
			update(0, n - 1, bts[cur[0]], 1, 1);
			ll x = query(0, n - 1, 0, bts[cur[0]] - 1, 1);
			ll y = query(0, n - 1, bts[cur[0]] + 1, n - 1, 1);
			ll prev  = ans;
			ans += occ.size() * (occ.size() + 1) / 2 - (x * (x + 1) / 2) - (y * (y + 1) / 2);
			if(ans < 0){
				cout << prev << '\n';
				cout << "HERE\n";
				return 0;
			}
			continue;
		}
		for(int i = 0; i < cur.size(); i++){
			update(0, n - 1, bts[cur[i]], 1, 1);
		}
		ll curv = 0;
		int mk = 0;
		for(int i = 0; i < cur.size(); i++){
			occ.insert(cur[i]);
			if(i == 0){
				//ll x = query(0, n - 1, bts[cur[i]] + 1, bts[cur[i + 1]] - 1, 1);
				ll y = query(0, n - 1, 0, bts[cur[i]] - 1, 1);
				if(y){
					mk++;
					curv += ((y * (y + 1) / 2));
				}
			}
			else if(i == cur.size() - 1){
				ll x = query(0, n - 1, bts[cur[i - 1]] + 1, bts[cur[i]] - 1, 1);
				ll y = query(0, n - 1, bts[cur[i]] + 1, n - 1, 1);
				//cerr << x << ' ' << y << '\n';
				if(x && y){
				//	cerr << x << " " << y << '\n';
					mk++;
					curv += ((x * (x + 1) / 2 ) +  (y * (y + 1) / 2));
				}
				else if(x){
					mk++;
					curv += (x * (x + 1) / 2);
				}
				else if(y){
					mk++;
					curv += (y * (y + 1) / 2);
				}
			}
			else{
				//ll x = query(0, n - 1, bts[cur[i - 1]] + 1, bts[cur[i]], 1);
				ll y = query(0, n - 1, bts[cur[i - 1]] + 1, bts[cur[i]] - 1, 1);
				if(y){
					mk++;
					curv += ((y * (y + 1)) / 2);;
				}
			}
		}
		//cerr << curv << '\n';
		//cerr << ans << '\n';
		//cerr << occ.size() << " " << curv << '\n';
		ans += occ.size() * (occ.size() + 1) / 2 - curv;

	}
	cout << ans << '\n';
}


#include<bits/stdc++.h>
#define MOD 1000000007
#define ll long long
#define mp make_pair
#define pb push_back
#define N 200010
using namespace std;
int tree[4 * N];
map<int, int> bts;
void update(int low, int high, int ind, int value, int node){
	if(low == high){
		tree[node] = value;
		return;
	}
	int mid = (low + high) / 2;
	if(ind <= mid)
		update(low, mid, ind, value, 2 * node);
	else update(mid + 1, high, ind, value, 2 * node + 1);
	tree[node] = tree[2 * node] + tree[2 * node + 1];
}
int query(int low, int high, int begin, int end, int node){
	if(low > end || high < begin || begin > end)
		return 0;
	if(low >= begin && high <= end)
		return tree[node];
	int mid = (low + high) / 2;
	return query(low, mid, begin, end, 2 * node) + query(mid + 1, high, begin, end, 2 * node + 1);
}
map<int, vector<int> > df;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	int n;
	cin >> n;
	vector<int> rt;
	for(int i = 0; i < n; i++){
		int x, y;
		cin >> x >> y;
		rt.pb(x);
		df[y].pb(x);
	}
	sort(rt.begin(), rt.end());
	for(int i = 0; i < rt.size(); i++){
		bts[rt[i]] = lower_bound(rt.begin(), rt.end(), rt[i]) - rt.begin();
	}
	ll ans = 0;
	for(auto it = df.rbegin(); it != df.rend(); it++){
		set<int> ry;
		vector<int> cur = it->second;
		if(cur.size() == 1){
			update(0, n - 1, bts[cur[0]], 1, 1);
			ll x = query(0, n - 1, 0, bts[cur[0]], 1);
			ll y = query(0, n - 1, bts[cur[0]], n - 1, 1);
			ans += x * y;
			continue;
		}
		for(int i = 0; i < cur.size(); i++){
			update(0, n - 1, bts[cur[i]], 1, 1);
		}
		for(int i = 0; i < cur.size(); i++){
			ry.insert(cur[i]);
			if(i == 0){
				ll x = query(0, n - 1, bts[cur[i]], bts[cur[i + 1]] - 1, 1);
				ll y = query(0, n - 1, 0, bts[cur[i]], 1);
				ans += x * y;
			}
			else if(i == cur.size() - 1){
				ll x = query(0, n - 1, bts[cur[i - 1]] + 1, bts[cur[i]], 1);
				ll y = query(0, n - 1, bts[cur[i]], n - 1, 1);
				ans += x * y;
			}
			else{
				ll x = query(0, n - 1, bts[cur[i - 1]] + 1, bts[cur[i]], 1);
				ll y = query(0, n - 1, bts[cur[i]], bts[cur[i + 1]] - 1, 1);
				ans += x * y;
			}
		}
		ans += ry.size() * (ry.size() - 1) / 2;

	}
	cout << ans << '\n';
}


#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 2e5+1;

int x[N], y[N];
vector <int> X, Y;
int main(){
    int n; cin >> n;
    X.reserve(n);
    Y.reserve(n);

    for (int i = 0; i < n; ++ i){
        cin >> x[i] >> y[i];
        X.push_back(x[i]);
        Y.push_back(y[i]);
    }

    sort(X.begin(), X.end());
    sort(Y.begin(), Y.end());
    map <int, int> idX, idY;

    for (int i = 0, c = 0; i < X.size(); ++ i){
        if (i && X[i] != X[i-1]) ++ c;
        idX[X[i]] = c;
    }

    for (int i = 0, c = 0; i < Y.size(); ++ i){
        if (i && Y[i] != Y[i-1]) ++ c;
        idY[Y[i]] = c;
    }

    for (int i = 0; i < n; ++ i){
        x[i] = idX[x[i]];
        y[i] = idY[y[i]];
    }

    X.clear();
    Y.clear();
    for (auto i: idX)
        X.push_back(i.first);
    for (auto i: idY)
        Y.push_back(i.first);

    vector <int> pos[Y.size()];
    for (int i = 0; i < n; ++ i)
        pos[y[i]].push_back(x[i]);
    for (int i = 0; i < Y.size(); ++ i)
        sort(pos[i].begin(), pos[i].end());

    ll ans = 0;
    for (int i = 0; i < n; ++ i){
        auto me = lower_bound(pos[y[i]].begin(), pos[y[i]].end(), x[i]);
        int l, r;
        if (me == pos[y[i]].begin())
            l = 0;
        else l = (*(me-1))+1;

        if (me+1 == pos[y[i]].end())
            r = X.size()-1;
        else r = (*(me+1))-1;

//        cout << i << " " << y[i] << " " << r << " " << x[i] << endl;
        ans += (int(X.size())-1-x[i]);
        ans += (r-x[i]+1)*1LL*(x[i]-l+1);
    }
    cout << ans << endl;
}


#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 2e5+1;

int f[N];
int st[N];
void mod(int i, int dt){
    for (; i < N; i |= i+1)
        f[i] += dt;
}
int get(int i){
    int res = 0;
    for (; i >= 0; i = (i&(i+1))-1)
        res += f[i];
    return res;
}

int x[N], y[N];
vector <int> X, Y;
int main(){
    int n; cin >> n;
    X.reserve(n);
    Y.reserve(n);

    for (int i = 0; i < n; ++ i){
        cin >> x[i] >> y[i];
        X.push_back(x[i]);
        Y.push_back(y[i]);
    }

    sort(X.begin(), X.end());
    sort(Y.begin(), Y.end());
    map <int, int> idX, idY;

    for (int i = 0, c = 0; i < X.size(); ++ i){
        if (i && X[i] != X[i-1]) ++ c;
        idX[X[i]] = c;
    }

    for (int i = 0, c = 0; i < Y.size(); ++ i){
        if (i && Y[i] != Y[i-1]) ++ c;
        idY[Y[i]] = c;
    }

    X.clear();
    Y.clear();

    for (int i = 0; i < n; ++ i){
        x[i] = idX[x[i]];
        y[i] = idY[y[i]];
    }

    vector <int> pos[idY.size()];
    for (int i = 0; i < n; ++ i)
        pos[y[i]].push_back(x[i]);

    ll ans = 0;
    for (int i = int(idY.size())-1; i >= 0; -- i){
        sort(pos[i].begin(), pos[i].end());

        for (int cx: pos[i]){
            if (st[cx]) continue;
            mod(cx, +1);
            st[cx] = 1;
        }

        for (int j = 0; j < pos[i].size(); ++ j){
            int px = (j?pos[i][j-1]:-1);
            int lft = get(pos[i][j])-get(px);
            int rht = get(N-1)-get(pos[i][j]-1);
            ans += lft*1LL*rht;
        }
    }
    cout << ans << "\n";
}


#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 2e5+1;

int x[N], y[N];
vector <int> X, Y;
int main(){
    int n; cin >> n;
    X.reserve(n);
    Y.reserve(n);

    for (int i = 0; i < n; ++ i){
        cin >> x[i] >> y[i];
        X.push_back(x[i]);
        Y.push_back(y[i]);
    }

    sort(X.begin(), X.end());
    sort(Y.begin(), Y.end());
    map <int, int> idX, idY;

    for (int i = 0, c = 0; i < X.size(); ++ i){
        if (i && X[i] != X[i-1]) ++ c;
        idX[X[i]] = c;
    }

    for (int i = 0, c = 0; i < Y.size(); ++ i){
        if (i && Y[i] != Y[i-1]) ++ c;
        idY[Y[i]] = c;
    }

    for (int i = 0; i < n; ++ i){
        x[i] = idX[x[i]];
        y[i] = idY[y[i]];
//        cout << "MM " << x[i] << " " << y[i] << endl;
    }

    vector <int> pos[Y.size()];
    for (int i = 0; i < n; ++ i)
        pos[y[i]].push_back(x[i]);
    for (int i = 0; i < Y.size(); ++ i)
        sort(pos[i].begin(), pos[i].end());

    ll ans = 0;
    for (int i = 0; i < n; ++ i){
        auto me = lower_bound(pos[y[i]].begin(), pos[y[i]].end(), x[i]);
        int l, r;
        if (me == pos[y[i]].begin())
            l = 0;
        else l = (*(me-1))+1;

        if (me+1 == pos[y[i]].end())
            r = int(idX.size())-1;
        else r = (*(me+1))-1;

//        cout << i << " " << x[i] << " " << y[i] << " " << l << " " << r << " " << X.size() << endl;
        ans += (x[i]-l+1)*1LL*(int(idX.size())-x[i]);
    }
    cout << ans << endl;
}


#include <bits/stdc++.h>

using namespace std;

constexpr int N = 1e6 + 10;

long long n, x[N], y[N], soton[N], have, ans, fen[N], last, what[N];
vector <long long> vv, who[N], vec;
bool is[N];

void add (int x, int val) {
    for (; x < N; x += x & (-x))
        fen[x] += val;
}

int get (int x) {
    int ret = 0;
    for (; x; x -= x & (-x))
        ret += fen[x];
    return ret;
}

/*
what[i] -> to x = i chanta noghte darim
who[i] -> to y = i che noghte haii darim
vec -> liste y haii ke toshon noghte hast
have -> tedade soton haii ke ta alan noghte toshon hast
*/

int main() {
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> x[i] >> y[i];
        vv.push_back (x[i]);
        vv.push_back (y[i]);
    }
    memset (soton, 63, sizeof (soton));
    vv.push_back (-N);
    sort (vv.begin(), vv.end());
    vv.resize (unique (vv.begin(), vv.end()) - vv.begin());
    for (int i = 0; i < n; i++) {
        x[i] = lower_bound (vv.begin(), vv.end(), x[i]) - vv.begin();
        y[i] = lower_bound (vv.begin(), vv.end(), y[i]) - vv.begin();
        what[x[i]]++;
        who[y[i]].push_back (x[i]);
        vec.push_back (y[i]);
    }
    for (int i = 0; i < N; i++)
        sort (who[i].begin(), who[i].end()), ans += what[i];
    sort (vec.begin(), vec.end());
    vec.resize (unique (vec.begin(), vec.end()) - vec.begin());
    while ((int)vec.size()) {
        long long pos = vec.back();
        vec.pop_back();
        for (auto i : who[pos]) {
            if (is[i])
                continue;
            have++;
        }
        ans += (have * (have - 1)) / 2;
        last = 0;
        who[pos].push_back (N - 10);
        for (auto i : who[pos]) {
            long long l = get (i - 1) - get (last);
            last = i;
            ans -= (l * (l - 1)) / 2;
            if (!is[i]) {
                add (i, 1);
                is[i] = true;
            }
        }
    }
    cout << ans << '\n';
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

constexpr int N = 1e6 + 10;

long long n, x[N], y[N], soton[N], have, ans, fen[N], last, what[N];
vector <long long> vv, who[N], vec;
bool is[N];

void add (int x, int val) {
    for (; x < N; x += x & (-x))
        fen[x] += val;
}

int get (int x) {
    int ret = 0;
    for (; x; x -= x & (-x))
        ret += fen[x];
    return ret;
}

/*
what[i] -> to x = i chanta noghte darim
who[i] -> to y = i che noghte haii darim
vec -> liste y haii ke toshon noghte hast
have -> tedade soton haii ke ta alan noghte toshon hast
*/

int main() {
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> x[i] >> y[i];
        vv.push_back (x[i]);
        vv.push_back (y[i]);
    }
    memset (soton, 63, sizeof (soton));
    vv.push_back (-N);
    sort (vv.begin(), vv.end());
    vv.resize (unique (vv.begin(), vv.end()) - vv.begin());
    for (int i = 0; i < n; i++) {
        x[i] = lower_bound (vv.begin(), vv.end(), x[i]) - vv.begin();
        y[i] = lower_bound (vv.begin(), vv.end(), y[i]) - vv.begin();
        what[x[i]]++;
        who[y[i]].push_back (x[i]);
        vec.push_back (y[i]);
    }
    for (int i = 0; i < N; i++)
        sort (who[i].begin(), who[i].end()), ans += what[i];
    sort (vec.begin(), vec.end());
    vec.resize (unique (vec.begin(), vec.end()) - vec.begin());
    while ((int)vec.size()) {
        long long pos = vec.back();
        vec.pop_back();
        for (auto i : who[pos]) {
            if (is[i])
                continue;
            have++;
        }
        ans += (have * (have - 1)) / 2;
        last = 0;
        who[pos].push_back (N - 10);
        for (auto i : who[pos]) {
            int l = get (i) - get (last);
            last = i;
            ans -= (l * (l - 1)) / 2;
            if (!is[i] && i != (N - 10)) {
                add (i, 1);
                is[i] = true;
            }
        }
    }
    cout << ans << '\n';
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

constexpr int N = 1e6 + 10;

long long n, x[N], y[N], soton[N], have, ans, fen[N], last, what[N];
vector <long long> vv, who[N], vec;
bool is[N];

void add (int x, int val) {
    for (; x < N; x += x & (-x))
        fen[x] += val;
}

int get (int x) {
    int ret = 0;
    for (; x; x -= x & (-x))
        ret += fen[x];
    return ret;
}

/*
what[i] -> to x = i chanta noghte darim
who[i] -> to y = i che noghte haii darim
vec -> liste y haii ke toshon noghte hast
have -> tedade soton haii ke ta alan noghte toshon hast
*/

int main() {
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> x[i] >> y[i];
        vv.push_back (x[i]);
        vv.push_back (y[i]);
    }
    memset (soton, 63, sizeof (soton));
    vv.push_back (-N);
    sort (vv.begin(), vv.end());
    vv.resize (unique (vv.begin(), vv.end()) - vv.begin());
    for (int i = 0; i < n; i++) {
        x[i] = lower_bound (vv.begin(), vv.end(), x[i]) - vv.begin();
        y[i] = lower_bound (vv.begin(), vv.end(), y[i]) - vv.begin();
        what[x[i]]++;
        who[y[i]].push_back (x[i]);
        vec.push_back (y[i]);
    }
    for (int i = 0; i < N; i++)
        sort (who[i].begin(), who[i].end()), ans += what[i];
    sort (vec.begin(), vec.end());
    vec.resize (unique (vec.begin(), vec.end()) - vec.begin());
    while ((int)vec.size()) {
        long long pos = vec.back();
        vec.pop_back();
        for (auto i : who[pos]) {
            if (is[i])
                continue;
            have++;
        }
        ans += (have * (have - 1)) / 2;
        last = 0;
        for (auto i : who[pos]) {
            int l = get (i) - get (last);
            last = i;
            ans -= (l * (l - 1)) / 2;
            if (!is[i]) {
                add (i, 1);
                is[i] = true;
            }
        }
    }
    cout << ans << '\n';
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

constexpr int N = 1e6 + 10;

long long n, x[N], y[N], soton[N], have, ans, fen[N], last, what[N];
vector <long long> vv, who[N], vec;
bool is[N];

void add (int x, int val) {
    for (; x < N; x += x & (-x))
        fen[x] += val;
}

int get (int x) {
    int ret = 0;
    for (; x; x -= x & (-x))
        ret += fen[x];
    return ret;
}

/*
what[i] -> to x = i chanta noghte darim
who[i] -> to y = i che noghte haii darim
vec -> liste y haii ke toshon noghte hast
have -> tedade soton haii ke ta alan noghte toshon hast
*/

int main() {
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> x[i] >> y[i];
        vv.push_back (x[i]);
        vv.push_back (y[i]);
    }
    memset (soton, 63, sizeof (soton));
    vv.push_back (-N);
    sort (vv.begin(), vv.end());
    vv.resize (unique (vv.begin(), vv.end()) - vv.begin());
    for (int i = 0; i < n; i++) {
        x[i] = lower_bound (vv.begin(), vv.end(), x[i]) - vv.begin();
        y[i] = lower_bound (vv.begin(), vv.end(), y[i]) - vv.begin();
        what[x[i]]++;
        who[y[i]].push_back (x[i]);
        vec.push_back (y[i]);
    }
    for (int i = 0; i < N; i++)
        sort (who[i].begin(), who[i].end()), ans += what[i];
    sort (vec.begin(), vec.end());
    vec.resize (unique (vec.begin(), vec.end()) - vec.begin());
    while ((int)vec.size()) {
        long long pos = vec.back();
        vec.pop_back();
        for (auto i : who[pos]) {
            if (is[i])
                continue;
            have++;
        }
        ans += (have * (have - 1)) / 2;
        last = 0;
        who[pos].push_back (N - 10);
        for (auto i : who[pos]) {
            int l = get (i - 1) - get (last);
            last = i;
            ans -= (l * (l - 1)) / 2;
            if (!is[i]) {
                add (i, 1);
                is[i] = true;
            }
        }
    }
    cout << ans << '\n';
    return 0;
}


#include<bits/stdc++.h>
using namespace std;

#define pb push_back
#define mp make_pair
#define ALL(x) (x).begin(),(x).end()
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e5 + 70;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-7;
const double PI = acos(-1.0);

int n, m, k;

struct Node {
	Node* ch[2];
	int v, r, cnt, sz;
	Node(int v, int c) :v(v), cnt(c), sz(c) { ch[0] = ch[1] = NULL; r = rand(); }
	bool operator < (const Node& rhs) const { return r < rhs.r; }
	int cmp(int x) const {
		if (x == v) return -1;
		return x < v ? 0 : 1;
	}
	void maintain() {
		sz = cnt;
		if (ch[0] != NULL) sz += ch[0]->sz;
		if (ch[1] != NULL) sz += ch[1]->sz;
	}
};

void rotate(Node*& o, int d) {
	Node* k = o->ch[d ^ 1];
	o->ch[d ^ 1] = k->ch[d];
	k->ch[d] = o;
	o->maintain(); k->maintain(); o = k;
}

void insert(Node*& o, int x, int c) {
	if (o == NULL) o = new Node(x, c);
	else {
		int d = o->cmp(x);
		if (d == -1) o->cnt += c;
		else {
			insert(o->ch[d], x, c);
			if (o->ch[d] > o) rotate(o, d ^ 1);
		}
		o->maintain();
	}
}

void remove(Node*& o, int x, int c) {
	int d = o->cmp(x);
	if (d == -1) {
		assert(o->cnt >= c);
		if (o->cnt - c > 0) o->cnt -= c;
		else {
			if (o->ch[0] != NULL && o->ch[1] != NULL) {
				int d2 = (o->ch[0] > o->ch[1] ? 1 : 0);
				rotate(o, d2); remove(o->ch[d2], x, c);
			}
			else {
				Node* u = o;
				if (o->ch[0] == NULL) o = o->ch[1];
				else o = o->ch[0];
				delete u;
			}
		}
	}
	else {
		remove(o->ch[d], x, c);
	}
	if (o != NULL) o->maintain();
}

int find(Node* o, int x) {
	while (o != NULL) {
		int d = o->cmp(x);
		if (d == -1) return o->cnt;
		o = o->ch[d];
	}
	return 0;
}

int getless(Node* o, int x) {
	int ans = 0;
	while (o != NULL) {
		int d = (x <= o->v ? 0 : 1);
		if (d) {
			ans += o->cnt;
			if (o->ch[d ^ 1] != NULL) ans += o->ch[d ^ 1]->sz;
		}
		o = o->ch[d];
	}
	return ans;
}

int kth(Node* o, int k) {
	assert(o != NULL && k >= 1 && k <= o->sz);
	int s = (o->ch[0] == NULL ? 0 : o->ch[0]->sz);
	if (k >= s + 1 && k <= s + o->cnt) return o->v;
	else if (k <= s) return kth(o->ch[0], k);
	else return kth(o->ch[1], k - s - o->cnt);
}

void mergeto(Node*& src, Node*& dest) {
    if (src == NULL) return;
	mergeto(src->ch[0], dest);
	mergeto(src->ch[1], dest);
	insert(dest, src->v, src->cnt);
	delete src;
	src = NULL;
}

void removetree(Node*& o) {
    if (o == NULL) return;
	removetree(o->ch[0]);
	removetree(o->ch[1]);
	delete o;
	o = NULL;
}

struct point{
    int x, y;
    bool operator < (const point& rhs){
        return y>rhs.y || (y==rhs.y && x<rhs.x);
    }
}a[maxn];

int main(){
    //freopen("in.txt", "r", stdin);
    while(cin>>n){
        for(int i=0;i<n;i++) scanf("%d %d",&a[i].x,&a[i].y);
        sort(a,a+n);

        Node* rt = NULL;
        ll ans=0; int s=0;
        while(s<n){
            int t = s; while(a[t].y == a[s].y) t++;

            int last = 0;
            for(int i=s;i<t;i++){
                int tmp = getless(rt,a[i].x);
                int v = tmp - last;
                if(i!=s) v -= find(rt, a[i-1].x);
                ans -= (ll)v*(v+1)/2;
                last = tmp;
            }
            int v = (rt == NULL?0:rt->sz) - last - find(rt, a[t-1].x);
            ans -= (ll)v*(v+1)/2;

            while(s!=t){
                if(find(rt,a[s].x) == 0) insert(rt,a[s].x,1);
                s++;
            }
            v = rt->sz;
            ans += (ll)v*(v+1)/2;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


#include<bits/stdc++.h>
using namespace std;

#define pb push_back
#define mp make_pair
#define ALL(x) (x).begin(),(x).end()
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e5 + 70;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-7;
const double PI = acos(-1.0);

int n, m, k;

struct Node {
	Node* ch[2];
	int v, r, cnt, sz;
	Node(int v, int c) :v(v), cnt(c), sz(c) { ch[0] = ch[1] = NULL; r = rand(); }
	bool operator < (const Node& rhs) const { return r < rhs.r; }
	int cmp(int x) const {
		if (x == v) return -1;
		return x < v ? 0 : 1;
	}
	void maintain() {
		sz = cnt;
		if (ch[0] != NULL) sz += ch[0]->sz;
		if (ch[1] != NULL) sz += ch[1]->sz;
	}
};

void rotate(Node*& o, int d) {
	Node* k = o->ch[d ^ 1];
	o->ch[d ^ 1] = k->ch[d];
	k->ch[d] = o;
	o->maintain(); k->maintain(); o = k;
}

void insert(Node*& o, int x, int c) {
	if (o == NULL) o = new Node(x, c);
	else {
		int d = o->cmp(x);
		if (d == -1) o->cnt += c;
		else {
			insert(o->ch[d], x, c);
			if (o->ch[d] > o) rotate(o, d ^ 1);
		}
		o->maintain();
	}
}

void remove(Node*& o, int x, int c) {
	int d = o->cmp(x);
	if (d == -1) {
		assert(o->cnt >= c);
		if (o->cnt - c > 0) o->cnt -= c;
		else {
			if (o->ch[0] != NULL && o->ch[1] != NULL) {
				int d2 = (o->ch[0] > o->ch[1] ? 1 : 0);
				rotate(o, d2); remove(o->ch[d2], x, c);
			}
			else {
				Node* u = o;
				if (o->ch[0] == NULL) o = o->ch[1];
				else o = o->ch[0];
				delete u;
			}
		}
	}
	else {
		remove(o->ch[d], x, c);
	}
	if (o != NULL) o->maintain();
}

int find(Node* o, int x) {
	while (o != NULL) {
		int d = o->cmp(x);
		if (d == -1) return o->cnt;
		o = o->ch[d];
	}
	return 0;
}

int getless(Node* o, int x) {
	int ans = 0;
	while (o != NULL) {
		int d = (x <= o->v ? 0 : 1);
		if (d) {
			ans += o->cnt;
			if (o->ch[d ^ 1] != NULL) ans += o->ch[d ^ 1]->sz;
		}
		o = o->ch[d];
	}
	return ans;
}

int kth(Node* o, int k) {
	assert(o != NULL && k >= 1 && k <= o->sz);
	int s = (o->ch[0] == NULL ? 0 : o->ch[0]->sz);
	if (k >= s + 1 && k <= s + o->cnt) return o->v;
	else if (k <= s) return kth(o->ch[0], k);
	else return kth(o->ch[1], k - s - o->cnt);
}

void mergeto(Node*& src, Node*& dest) {
    if (src == NULL) return;
	mergeto(src->ch[0], dest);
	mergeto(src->ch[1], dest);
	insert(dest, src->v, src->cnt);
	delete src;
	src = NULL;
}

void removetree(Node*& o) {
    if (o == NULL) return;
	removetree(o->ch[0]);
	removetree(o->ch[1]);
	delete o;
	o = NULL;
}

struct point{
    int x, y;
    bool operator < (const point& rhs){
        return y>rhs.y || (y==rhs.y && x<rhs.x);
    }
}a[maxn];

int main(){
    //freopen("in.txt", "r", stdin);
    while(cin>>n){
        for(int i=0;i<n;i++) scanf("%d %d",&a[i].x,&a[i].y);
        sort(a,a+n);

        Node* rt = NULL;
        ll ans=0; int s=0;
        while(s<n){
            int l = getless(rt,a[s].x);
            int t = s; while(a[t].y == a[s].y) t++;
            int r = (rt==NULL?0:rt->sz) - getless(rt,a[t-1].x+1);
            while(s!=t){
                if(find(rt,a[s].x) == 0) insert(rt,a[s].x,1);
                s++;
            }
            int v = rt->sz;
            ans+=(ll)v*(v+1)/2;
            ans-=(ll)l*(l+1)/2;
            ans-=(ll)r*(r+1)/2;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


#include<bits/stdc++.h>
using namespace std;

#define pb push_back
#define mp make_pair
#define ALL(x) (x).begin(),(x).end()
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e5 + 70;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-7;
const double PI = acos(-1.0);

int n, m, k;

struct Node {
	Node* ch[2];
	int v, r, cnt, sz;
	Node(int v, int c) :v(v), cnt(c), sz(c) { ch[0] = ch[1] = NULL; r = rand(); }
	bool operator < (const Node& rhs) const { return r < rhs.r; }
	int cmp(int x) const {
		if (x == v) return -1;
		return x < v ? 0 : 1;
	}
	void maintain() {
		sz = cnt;
		if (ch[0] != NULL) sz += ch[0]->sz;
		if (ch[1] != NULL) sz += ch[1]->sz;
	}
};

void rotate(Node*& o, int d) {
	Node* k = o->ch[d ^ 1];
	o->ch[d ^ 1] = k->ch[d];
	k->ch[d] = o;
	o->maintain(); k->maintain(); o = k;
}

void insert(Node*& o, int x, int c) {
	if (o == NULL) o = new Node(x, c);
	else {
		int d = o->cmp(x);
		if (d == -1) o->cnt += c;
		else {
			insert(o->ch[d], x, c);
			if (o->ch[d] > o) rotate(o, d ^ 1);
		}
		o->maintain();
	}
}

void remove(Node*& o, int x, int c) {
	int d = o->cmp(x);
	if (d == -1) {
		assert(o->cnt >= c);
		if (o->cnt - c > 0) o->cnt -= c;
		else {
			if (o->ch[0] != NULL && o->ch[1] != NULL) {
				int d2 = (o->ch[0] > o->ch[1] ? 1 : 0);
				rotate(o, d2); remove(o->ch[d2], x, c);
			}
			else {
				Node* u = o;
				if (o->ch[0] == NULL) o = o->ch[1];
				else o = o->ch[0];
				delete u;
			}
		}
	}
	else {
		remove(o->ch[d], x, c);
	}
	if (o != NULL) o->maintain();
}

int find(Node* o, int x) {
	while (o != NULL) {
		int d = o->cmp(x);
		if (d == -1) return o->cnt;
		o = o->ch[d];
	}
	return 0;
}

int getless(Node* o, int x) {
	int ans = 0;
	while (o != NULL) {
		int d = (x <= o->v ? 0 : 1);
		if (d) {
			ans += o->cnt;
			if (o->ch[d ^ 1] != NULL) ans += o->ch[d ^ 1]->sz;
		}
		o = o->ch[d];
	}
	return ans;
}

int kth(Node* o, int k) {
	assert(o != NULL && k >= 1 && k <= o->sz);
	int s = (o->ch[0] == NULL ? 0 : o->ch[0]->sz);
	if (k >= s + 1 && k <= s + o->cnt) return o->v;
	else if (k <= s) return kth(o->ch[0], k);
	else return kth(o->ch[1], k - s - o->cnt);
}

void mergeto(Node*& src, Node*& dest) {
    if (src == NULL) return;
	mergeto(src->ch[0], dest);
	mergeto(src->ch[1], dest);
	insert(dest, src->v, src->cnt);
	delete src;
	src = NULL;
}

void removetree(Node*& o) {
    if (o == NULL) return;
	removetree(o->ch[0]);
	removetree(o->ch[1]);
	delete o;
	o = NULL;
}

struct point{
    int x, y;
    bool operator < (const point& rhs){
        return y>rhs.y || (y==rhs.y && x<rhs.x);
    }
}a[maxn];

int main(){
    //freopen("in.txt", "r", stdin);
    while(cin>>n){
        for(int i=0;i<n;i++) scanf("%d %d",&a[i].x,&a[i].y);
        sort(a,a+n);

        Node* rt = NULL;
        ll ans=0; int s=0;
        while(s<n){
            int t = s; while(a[t].y == a[s].y) t++;

            for(int i=s;i<t;i++){
                int v = getless(rt,a[i].x);
                if(i!=s) v -= getless(rt, a[i-1].x+1);
                ans -= (ll)v*(v+1)/2;
            }
            int v = (rt == NULL?0:rt->sz) - getless(rt, a[t-1].x+1);
            ans -= (ll)v*(v+1)/2;

            while(s!=t){
                if(find(rt,a[s].x) == 0) insert(rt,a[s].x,1);
                s++;
            }
            v = rt->sz;
            ans += (ll)v*(v+1)/2;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


#include<bits/stdc++.h>
using namespace std;

#define pb push_back
#define mp make_pair
#define ALL(x) (x).begin(),(x).end()
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e5 + 70;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-7;
const double PI = acos(-1.0);

int n, m, k;

struct Node {
	Node* ch[2];
	int v, r, cnt, sz;
	Node(int v, int c) :v(v), cnt(c), sz(c) { ch[0] = ch[1] = NULL; r = rand(); }
	int cmp(int x) const {
		if (x == v) return -1;
		return x < v ? 0 : 1;
	}
	void maintain() {
		sz = cnt;
		if (ch[0] != NULL) sz += ch[0]->sz;
		if (ch[1] != NULL) sz += ch[1]->sz;
	}
};

void rotate(Node*& o, int d) {
	Node* k = o->ch[d ^ 1];
	o->ch[d ^ 1] = k->ch[d];
	k->ch[d] = o;
	o->maintain(); k->maintain(); o = k;
}

void insert(Node*& o, int x, int c) {
	if (o == NULL) o = new Node(x, c);
	else {
		int d = o->cmp(x);
		if (d == -1) o->cnt += c;
		else {
			insert(o->ch[d], x, c);
			if (o->ch[d]->r > o->r) rotate(o, d ^ 1);
		}
		o->maintain();
	}
}

void remove(Node*& o, int x, int c) {
	int d = o->cmp(x);
	if (d == -1) {
		assert(o->cnt >= c);
		if (o->cnt - c > 0) o->cnt -= c;
		else {
			if (o->ch[0] != NULL && o->ch[1] != NULL) {
				int d2 = (o->ch[0] > o->ch[1] ? 1 : 0);
				rotate(o, d2); remove(o->ch[d2], x, c);
			}
			else {
				Node* u = o;
				if (o->ch[0] == NULL) o = o->ch[1];
				else o = o->ch[0];
				delete u;
			}
		}
	}
	else {
		remove(o->ch[d], x, c);
	}
	if (o != NULL) o->maintain();
}

int find(Node* o, int x) {
	while (o != NULL) {
		int d = o->cmp(x);
		if (d == -1) return o->cnt;
		o = o->ch[d];
	}
	return 0;
}

int getless(Node* o, int x) {
	int ans = 0;
	while (o != NULL) {
		int d = (x <= o->v ? 0 : 1);
		if (d) {
			ans += o->cnt;
			if (o->ch[d ^ 1] != NULL) ans += o->ch[d ^ 1]->sz;
		}
		o = o->ch[d];
	}
	return ans;
}

int kth(Node* o, int k) {
	assert(o != NULL && k >= 1 && k <= o->sz);
	int s = (o->ch[0] == NULL ? 0 : o->ch[0]->sz);
	if (k >= s + 1 && k <= s + o->cnt) return o->v;
	else if (k <= s) return kth(o->ch[0], k);
	else return kth(o->ch[1], k - s - o->cnt);
}

void mergeto(Node*& src, Node*& dest) {
    if (src == NULL) return;
	mergeto(src->ch[0], dest);
	mergeto(src->ch[1], dest);
	insert(dest, src->v, src->cnt);
	delete src;
	src = NULL;
}

void removetree(Node*& o) {
    if (o == NULL) return;
	removetree(o->ch[0]);
	removetree(o->ch[1]);
	delete o;
	o = NULL;
}

int height(Node* o){
    if(o == NULL) return 0;
    return 1 + max(height(o -> ch[0]), height(o -> ch[1]));
}

ll cost(Node* o, int d = 1){
    if(o == NULL) return 0;
    return (ll)d*o->cnt + cost(o->ch[0], d+1) + cost(o->ch[1], d+1);
}

void check(Node* o){
    if(o == NULL) return;
    cout<<o->v<<" "<<o->r<<endl;
    check(o->ch[0]); check(o->ch[1]);
}

struct point{
    int x, y;
    bool operator < (const point& rhs){
        return y>rhs.y || (y==rhs.y && x<rhs.x);
    }
}a[maxn];

int main(){
    //freopen("in.txt", "r", stdin);
    while(cin>>n){
        for(int i=0;i<n;i++) scanf("%d %d",&a[i].x,&a[i].y);
        sort(a,a+n);

        Node* rt = NULL;
        ll ans=0; int s=0;
        while(s<n){
            int t = s; while(a[t].y == a[s].y) t++;

            for(int i=s;i<t;i++){
                int v = getless(rt,a[i].x);
                if(i!=s) v -= getless(rt, a[i-1].x+1);
                ans -= (ll)v*(v+1)/2;
            }
            int v = (rt == NULL?0:rt->sz) - getless(rt, a[t-1].x+1);
            ans -= (ll)v*(v+1)/2;

            while(s!=t){
                if(find(rt,a[s].x) == 0) insert(rt,a[s].x,1);
                s++;
            }
            v = rt->sz;
            ans += (ll)v*(v+1)/2;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


#include<bits/stdc++.h>
using namespace std;

#define pb push_back
#define mp make_pair
#define ALL(x) (x).begin(),(x).end()
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e5 + 70;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-7;
const double PI = acos(-1.0);

int n, m, k;

struct point{
    int x, y;
    bool operator < (const point& rhs){
        return y>rhs.y || (y==rhs.y && x<rhs.x);
    }
}a[maxn];

int xs[maxn];

ll C[maxn]; bool vis[maxn];

void add(int x, int v){
	while(x <= n){
        C[x] += v;
        x += x & -x;
	}
}

ll pre(int x){
	ll s = 0;
	while(x){
        s += C[x];
        x -= x & -x;
	}
	return s;
}

int main(){
    //freopen("in.txt", "r", stdin);
    while(cin>>n){
        for(int i=0;i<n;i++){
            scanf("%d %d",&a[i].x,&a[i].y);
            xs[i]=a[i].x;
        }
        sort(xs,xs+n);
        m=unique(xs,xs+n)-xs;
        for(int i=0;i<n;i++) a[i].x=lower_bound(xs,xs+m,a[i].x)-xs+1;
        sort(a,a+n);

        memset(C,0,(m+1)*sizeof(ll));
        memset(vis,0,(m+1));
        ll ans=0; int s=0;
        while(s<n){
            int t = s; while(a[t].y == a[s].y) t++;

            for(int i=s;i<t;i++){
                int v = pre(a[i].x-1);
                if(i!=s) v -= pre(a[i-1].x);
                ans -= (ll)v*(v+1)/2;
            }
            int v = pre(m) - pre(a[t-1].x);
            ans -= (ll)v*(v+1)/2;

            while(s!=t){
                if(!vis[a[s].x]){
                    add(a[s].x,1);
                    vis[a[s].x]=1;
                }
                s++;
            }
            v = pre(m);
            ans += (ll)v*(v+1)/2;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


#include<bits/stdc++.h>
using namespace std;

#define pb push_back
#define mp make_pair
#define ALL(x) (x).begin(),(x).end()
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e5 + 70;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-7;
const double PI = acos(-1.0);

int n, m, k;

struct Node {
	Node* ch[2];
	int v, r, cnt, sz;
	Node(int v, int c) :v(v), cnt(c), sz(c) { ch[0] = ch[1] = NULL; r = rand(); }
	bool operator < (const Node& rhs) const { return r < rhs.r; }
	int cmp(int x) const {
		if (x == v) return -1;
		return x < v ? 0 : 1;
	}
	void maintain() {
		sz = cnt;
		if (ch[0] != NULL) sz += ch[0]->sz;
		if (ch[1] != NULL) sz += ch[1]->sz;
	}
};

void rotate(Node*& o, int d) {
	Node* k = o->ch[d ^ 1];
	o->ch[d ^ 1] = k->ch[d];
	k->ch[d] = o;
	o->maintain(); k->maintain(); o = k;
}

void insert(Node*& o, int x, int c) {
	if (o == NULL) o = new Node(x, c);
	else {
		int d = o->cmp(x);
		if (d == -1) o->cnt += c;
		else {
			insert(o->ch[d], x, c);
			if (o->ch[d] > o) rotate(o, d ^ 1);
		}
		o->maintain();
	}
}

void remove(Node*& o, int x, int c) {
	int d = o->cmp(x);
	if (d == -1) {
		assert(o->cnt >= c);
		if (o->cnt - c > 0) o->cnt -= c;
		else {
			if (o->ch[0] != NULL && o->ch[1] != NULL) {
				int d2 = (o->ch[0] > o->ch[1] ? 1 : 0);
				rotate(o, d2); remove(o->ch[d2], x, c);
			}
			else {
				Node* u = o;
				if (o->ch[0] == NULL) o = o->ch[1];
				else o = o->ch[0];
				delete u;
			}
		}
	}
	else {
		remove(o->ch[d], x, c);
	}
	if (o != NULL) o->maintain();
}

int find(Node* o, int x) {
	while (o != NULL) {
		int d = o->cmp(x);
		if (d == -1) return o->cnt;
		o = o->ch[d];
	}
	return 0;
}

int getless(Node* o, int x) {
	int ans = 0;
	while (o != NULL) {
		int d = (x <= o->v ? 0 : 1);
		if (d) {
			ans += o->cnt;
			if (o->ch[d ^ 1] != NULL) ans += o->ch[d ^ 1]->sz;
		}
		o = o->ch[d];
	}
	return ans;
}

int kth(Node* o, int k) {
	assert(o != NULL && k >= 1 && k <= o->sz);
	int s = (o->ch[0] == NULL ? 0 : o->ch[0]->sz);
	if (k >= s + 1 && k <= s + o->cnt) return o->v;
	else if (k <= s) return kth(o->ch[0], k);
	else return kth(o->ch[1], k - s - o->cnt);
}

void mergeto(Node*& src, Node*& dest) {
    if (src == NULL) return;
	mergeto(src->ch[0], dest);
	mergeto(src->ch[1], dest);
	insert(dest, src->v, src->cnt);
	delete src;
	src = NULL;
}

void removetree(Node*& o) {
    if (o == NULL) return;
	removetree(o->ch[0]);
	removetree(o->ch[1]);
	delete o;
	o = NULL;
}

struct point{
    int x, y;
    bool operator < (const point& rhs){
        return y>rhs.y || (y==rhs.y && x<rhs.x);
    }
}a[maxn];

int main(){
    //freopen("in.txt", "r", stdin);
    srand(time(NULL));
    while(cin>>n){
        for(int i=0;i<n;i++) scanf("%d %d",&a[i].x,&a[i].y);
        sort(a,a+n);

        Node* rt = NULL;
        ll ans=0; int s=0;
        while(s<n){
            int t = s; while(a[t].y == a[s].y) t++;

            int last = 0;
            for(int i=s;i<t;i++){
                int tmp = getless(rt,a[i].x);
                int v = tmp - last;
                if(i!=s) v -= find(rt, a[i-1].x);
                ans -= (ll)v*(v+1)/2;
                last = tmp;
            }
            int v = (rt == NULL?0:rt->sz) - last - find(rt, a[t-1].x);
            ans -= (ll)v*(v+1)/2;

            while(s!=t){
                if(find(rt,a[s].x) == 0) insert(rt,a[s].x,1);
                s++;
            }
            v = rt->sz;
            ans += (ll)v*(v+1)/2;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


#include<bits/stdc++.h>
using namespace std;

#define pb push_back
#define mp make_pair
#define ALL(x) (x).begin(),(x).end()
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e5 + 70;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-7;
const double PI = acos(-1.0);

int n, m, k;

struct point{
    int x, y;
    bool operator < (const point& rhs){
        return y>rhs.y || (y==rhs.y && x<rhs.x);
    }
}a[maxn];

int xs[maxn];

ll C[maxn]; bool vis[maxn];

void add(int x, int v){
	while(x <= n){
        C[x] += v;
        x += x & -x;
	}
}

ll pre(int x){
	ll s = 0;
	while(x){
        s += C[x];
        x -= x & -x;
	}
	return s;
}

int main(){
    //freopen("in.txt", "r", stdin);
    while(cin>>n){
        for(int i=0;i<n;i++){
            scanf("%d %d",&a[i].x,&a[i].y);
            xs[i]=a[i].x;
        }
        sort(xs,xs+n);
        m=unique(xs,xs+n)-xs;
        for(int i=0;i<n;i++) a[i].x=lower_bound(xs,xs+m,a[i].x)-xs+1;
        sort(a,a+n);

        memset(C,0,(m+1)*sizeof(ll));
        memset(vis,0,(m+1));
        ll ans=0; int s=0;
        while(s<n){
            int t = s; while(a[t].y == a[s].y) t++;

            for(int i=s;i<t;i++){
                int v = pre(a[i].x-1);
                if(i!=s) v -= pre(a[i-1].x+1);
                ans -= (ll)v*(v+1)/2;
            }
            int v = pre(m) - pre(a[t-1].x+1);
            ans -= (ll)v*(v+1)/2;

            while(s!=t){
                if(!vis[a[s].x]){
                    add(a[s].x,1);
                    vis[a[s].x]=1;
                }
                s++;
            }
            v = pre(m);
            ans += (ll)v*(v+1)/2;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<int, int> Pi;
typedef pair<ll, ll> Pll;

// #define TT() printf("%.4f sec\n", (double) clock() / CLOCKS_PER_SEC )
#define Fi first
#define Se second
#define rep(i, n) for(ll i=0;i<n;i++)
#define repp(i, n) for(ll i=1;i<=n;i++)

#define INF 987654321
#define IINF 987654321987654321

struct segtree{
	ll x; vector<ll> da;
	void init(ll n){
		x = 1;
		while(x < n) x <<= 1;
		da.resize(2*x+10);
	}

	void upd(ll pos){
		pos += x-1;
		if(da[pos] > 0) return;
		while(pos >= 1){
			da[pos]++; pos >>= 1;
		}
	}

	ll query(ll l, ll r){
		if(r <= 0) return 0;
		l += x-1; r += x-1;
		ll ans = 0;
		while(l <= r){
			if(l&1) ans += da[l++];
			if(!(r&1)) ans += da[r--];
			l >>= 1; r >>= 1;
		}
		return ans;
	}

}Segtree;

ll n;
vector<ll> xx,yy;
vector<ll> ss[200100];
vector<Pll> pt;

int main(){
	scanf("%lld",&n);
	repp(i,n){
		ll x,y; scanf("%lld%lld",&x,&y);
		pt.push_back({x,y});
		xx.push_back(x);
		yy.push_back(y);
	}
	sort(xx.begin(),xx.end());
	sort(yy.begin(),yy.end());
	yy.erase(unique(yy.begin(),yy.end()),yy.end());
	xx.erase(unique(xx.begin(),xx.end()),xx.end());
	ll szx = (ll)xx.size();
	ll szy = (ll)yy.size();

	Segtree.init(szx+10);
	for(auto e : pt){
		int px = lower_bound(xx.begin(),xx.end(),e.Fi) - xx.begin() + 1;
		int py = lower_bound(yy.begin(),yy.end(),e.Se) - yy.begin() + 1;
		ss[py].push_back(px);
	}
	ll ans = 0;
	for(int i = szy; i >= 1; i--){
		ll aaa = (ll)ss[i].size();
		sort(ss[i].begin(),ss[i].end());
		vector<ll> v;
		ll pv = 1;
		for(auto e : ss[i]){
			v.push_back(Segtree.query(pv,e-1));
			pv = e+1;
		}
		v.push_back(Segtree.query(ss[i][aaa-1]+1,szx));

		// for(auto e : v) printf("%lld ",e);
		// printf("\n");

		ll su = 0;
		for(auto e : v) su += e;
		su += aaa;
		ans = ans + (su*(su+1)/2);
		for(auto e : v){
			ans = ans - (e*(e+1)/2);
		}
		for(auto e : ss[i]) Segtree.upd(e);
	}
	printf("%lld\n",ans);

	return 0;
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<int, int> Pi;
typedef pair<ll, ll> Pll;

// #define TT() printf("%.4f sec\n", (double) clock() / CLOCKS_PER_SEC )
#define Fi first
#define Se second
#define rep(i, n) for(ll i=0;i<n;i++)
#define repp(i, n) for(ll i=1;i<=n;i++)

#define INF 987654321
#define IINF 987654321987654321

struct segtree{
	ll x; vector<ll> da;
	void init(ll n){
		x = 1;
		while(x < n) x <<= 1;
		da.resize(2*x+10);
	}

	void upd(ll pos){
		pos += x-1;
		if(da[pos] > 0) return;
		while(pos >= 1){
			da[pos]++; pos >>= 1;
		}
	}

	ll query(ll l, ll r){
		if(r <= 0) return 0;
		l += x-1; r += x-1;
		ll ans = 0;
		while(l <= r){
			if(l&1) ans += da[l++];
			if(!(r&1)) ans += da[r--];
			l >>= 1; r >>= 1;
		}
		return ans;
	}

}Segtree;

ll n;
vector<ll> xx,yy;
vector<ll> ss[200100];
vector<Pll> pt;

int main(){
	scanf("%lld",&n);
	repp(i,n){
		ll x,y; scanf("%lld%lld",&x,&y);
		pt.push_back({x,y});
		xx.push_back(x);
		yy.push_back(y);
	}
	sort(xx.begin(),xx.end());
	sort(yy.begin(),yy.end());
	yy.erase(unique(yy.begin(),yy.end()),yy.end());
	xx.erase(unique(xx.begin(),xx.end()),xx.end());
	ll szx = (ll)xx.size();
	ll szy = (ll)yy.size();

	Segtree.init(szx);
	for(auto e : pt){
		int px = lower_bound(xx.begin(),xx.end(),e.Fi) - xx.begin() + 1;
		int py = lower_bound(yy.begin(),yy.end(),e.Se) - yy.begin() + 1;
		ss[py].push_back(px);
	}
	ll ans = 0;
	for(int i = szy; i >= 1; i--){
		ll aaa = (ll)ss[i].size();
		vector<ll> v;
		ll pv = 1;
		for(auto e : ss[i]){
			v.push_back(Segtree.query(pv,e-1));
			pv = e+1;
		}
		v.push_back(Segtree.query(ss[i][aaa-1]+1,szx));

		// for(auto e : v) printf("%lld ",e);
		// printf("\n");

		ll su = 0;
		for(auto e : v) su += e;
		su += aaa;
		ans = ans + (su*(su+1)/2);
		for(auto e : v){
			ans = ans - (e*(e+1)/2);
		}
		for(auto e : ss[i]) Segtree.upd(e);
	}
	printf("%lld\n",ans);

	return 0;
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<int, int> Pi;
typedef pair<ll, ll> Pll;

// #define TT() printf("%.4f sec\n", (double) clock() / CLOCKS_PER_SEC )
#define Fi first
#define Se second
#define rep(i, n) for(ll i=0;i<n;i++)
#define repp(i, n) for(ll i=1;i<=n;i++)

#define INF 987654321
#define IINF 987654321987654321

struct segtree{
	ll x; vector<ll> da;
	void init(ll n){
		x = 1;
		while(x < n) x <<= 1;
		da.resize(2*x+10);
	}

	void upd(ll pos){
		pos += x-1;
		if(da[pos] > 0) return;
		while(pos >= 1){
			da[pos]++; pos >>= 1;
		}
	}

	ll query(ll l, ll r){
		if(r <= 0) return 0;
		l += x-1; r += x-1;
		ll ans = 0;
		while(l <= r){
			if(l&1) ans += da[l++];
			if(!(r&1)) ans += da[r--];
			l >>= 1; r >>= 1;
		}
		return ans;
	}

}Segtree;

ll n;
vector<ll> xx,yy;
vector<ll> ss[200100];
vector<Pll> pt;

int main(){
	scanf("%lld",&n);
	repp(i,n){
		ll x,y; scanf("%lld%lld",&x,&y);
		pt.push_back({x,y});
		xx.push_back(x);
		yy.push_back(y);
	}
	sort(xx.begin(),xx.end());
	sort(yy.begin(),yy.end());
	yy.erase(unique(yy.begin(),yy.end()),yy.end());
	xx.erase(unique(xx.begin(),xx.end()),xx.end());
	ll szx = (ll)xx.size();
	ll szy = (ll)yy.size();

	Segtree.init(szx+10);
	for(auto e : pt){
		int px = lower_bound(xx.begin(),xx.end(),e.Fi) - xx.begin() + 1;
		int py = lower_bound(yy.begin(),yy.end(),e.Se) - yy.begin() + 1;
		ss[py].push_back(px);
	}
	ll ans = 0;
	for(int i = szy; i >= 1; i--){
		ll aaa = (ll)ss[i].size();
		vector<ll> v;
		ll pv = 1;
		for(auto e : ss[i]){
			v.push_back(Segtree.query(pv,e-1));
			pv = e+1;
		}
		v.push_back(Segtree.query(ss[i][aaa-1]+1,szx));

		// for(auto e : v) printf("%lld ",e);
		// printf("\n");

		ll su = 0;
		for(auto e : v) su += e;
		su += aaa;
		ans = ans + (su*(su+1)/2);
		for(auto e : v){
			ans = ans - (e*(e+1)/2);
		}
		for(auto e : ss[i]) Segtree.upd(e);
	}
	printf("%lld\n",ans);

	return 0;
}


#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<int, int> Pi;
typedef pair<ll, ll> Pll;

// #define TT() printf("%.4f sec\n", (double) clock() / CLOCKS_PER_SEC )
#define Fi first
#define Se second
#define rep(i, n) for(ll i=0;i<n;i++)
#define repp(i, n) for(ll i=1;i<=n;i++)

#define INF 987654321
#define IINF 987654321987654321

struct segtree{
	int x; vector<ll> da;
	void init(int n){
		x = 1;
		while(x < n) x <<= 1;
		da.resize(2*x+10);
	}

	void upd(int pos){
		pos += x-1;
		if(da[pos] > 0) return;
		while(pos >= 1){
			da[pos]++; pos >>= 1;
		}
	}

	ll query(int l, int r){
		if(r <= 0) return 0;
		l += x-1; r += x-1;
		ll ans = 0;
		while(l <= r){
			if(l&1) ans += da[l++];
			if(!(r&1)) ans += da[r--];
			l >>= 1; r >>= 1;
		}
		return ans;
	}

}Segtree;

int n;
vector<ll> xx,yy;
vector<ll> ss[200100];
vector<Pll> pt;

int main(){
	scanf("%d",&n);
	repp(i,n){
		ll x,y; scanf("%lld%lld",&x,&y);
		pt.push_back({x,y});
		xx.push_back(x);
		yy.push_back(y);
	}
	sort(xx.begin(),xx.end());
	sort(yy.begin(),yy.end());
	yy.erase(unique(yy.begin(),yy.end()),yy.end());
	xx.erase(unique(xx.begin(),xx.end()),xx.end());
	int szx = (int)xx.size();
	int szy = (int)yy.size();

	Segtree.init(szx);
	for(auto e : pt){
		int px = lower_bound(xx.begin(),xx.end(),e.Fi) - xx.begin() + 1;
		int py = lower_bound(yy.begin(),yy.end(),e.Se) - yy.begin() + 1;
		ss[py].push_back(px);
	}
	ll ans = 0;
	for(int i = szy; i >= 1; i--){
		ll aaa = (ll)ss[i].size();
		vector<ll> v;
		int pv = 1;
		for(auto e : ss[i]){
			v.push_back(Segtree.query(pv,e-1));
			pv = e+1;
		}
		v.push_back(Segtree.query(ss[i][aaa-1]+1,szx));

		// for(auto e : v) printf("%lld ",e);
		// printf("\n");

		ll su = 0;
		for(auto e : v) su += e;
		su += aaa;
		ans = ans + (su*(su+1)/2);
		for(auto e : v){
			ans = ans - (e*(e+1)/2);
		}
		for(auto e : ss[i]) Segtree.upd(e);
	}
	printf("%lld\n",ans);

	return 0;
}


///If I see that thing, I'ma squash it
///and rip the wings of it off
#pragma GCC optimize ("O3")
#include "bits/stdc++.h"
using namespace std;
#define pb push_back
using ll = long long;
const int N = 1e5 + 5, MOD = 1e9 + 7;
int bit[N+N];
vector<int> ord;
void add(int idx){
  for(++idx; idx < N;idx+=idx&-idx)bit[idx]++;
}
int get(int idx){
  int ret = 0;
  for(++idx;idx;idx-=idx&-idx)ret += bit[idx];
  return ret;
}
int main(){
#ifdef ONLINE_JUDGE
  ios_base::sync_with_stdio(0); cin.tie(0);
#endif // ONLINE_JUDGE

    int n;
    cin >> n;

    vector<pair<int,int>> vp(n);

    for(int i = 0; i < n; i++){
      cin >> vp[i].first >> vp[i].second;
      ord.pb(vp[i].first);
    }
    sort(ord.begin(),ord.end());
    ord.resize(unique(ord.begin(),ord.end()) - ord.begin());
    sort(vp.begin(),vp.end(),[](pair<int,int>a, pair<int,int> b){
          if(a.second==b.second)
            return a.first < b.first;
          return a.second > b.second;
         });
    long long ans = 0;
    set<int>xs;
    for(int i = 0, j = 0; i < n; i = j){
      vector<int> go;
      for(; vp[i].second == vp[j].second; j++){
          int idx = lower_bound(ord.begin(),ord.end(),vp[j].first)-ord.begin();
          go.pb(idx);
      }
      for(int j = 0; j < go.size(); j++){
        int nxt = ord.size();
        int idx = go[j];
        if(j != go.size()-1)
          nxt = go[j + 1] - 1;
        if(xs.count(idx) == 0){
          xs.insert(idx);
          add(idx);
        }
        int lft = get(nxt) - get(idx-1);
        int rt = get(idx);
        ans += 1ll * lft * rt;
      }
    }

    cout << ans << '\n';

    return 0;
}


///If I see that thing, I'ma squash it
///and rip the wings of it off
#pragma GCC optimize ("O3")
#include "bits/stdc++.h"
using namespace std;
#define pb push_back
using ll = long long;
const int N = 2e5 + 5, MOD = 1e9 + 7;
int bit[N];
vector<int> ord;
void add(int idx){
  for(++idx; idx < N;idx+=idx&-idx)bit[idx]++;
}
int get(int idx){
  int ret = 0;
  for(++idx;idx;idx-=idx&-idx)ret += bit[idx];
  return ret;
}
int main(){
#ifdef ONLINE_JUDGE
  ios_base::sync_with_stdio(0); cin.tie(0);
#endif // ONLINE_JUDGE

    int n;
    cin >> n;

    vector<pair<int,int>> vp(n);

    for(int i = 0; i < n; i++){
      cin >> vp[i].first >> vp[i].second;
      ord.pb(vp[i].first);
    }
    sort(ord.begin(),ord.end());
    ord.resize(unique(ord.begin(),ord.end()) - ord.begin());
    sort(vp.begin(),vp.end(),[](pair<int,int>a, pair<int,int> b){
          if(a.second==b.second)
            return a.first < b.first;
          return a.second > b.second;
         });
    long long ans = 0;
    set<int>xs;
    for(int i = 0, j = 0; i < n; i = j){
      vector<int> go;
      for(; vp[i].second == vp[j].second; j++){
          int idx = lower_bound(ord.begin(),ord.end(),vp[j].first)-ord.begin();
          go.pb(idx);
      }
      for(int j = 0; j < go.size(); j++){
        int nxt = ord.size();
        int idx = go[j];
        if(j != go.size()-1)
          nxt = go[j + 1] - 1;
        if(xs.count(idx) == 0){
          xs.insert(idx);
          add(idx);
        }
        int lft = get(nxt) - get(idx-1);
        int rt = get(idx);
        ans += 1ll * lft * rt;
      }
    }

    cout << ans << '\n';

    return 0;
}


///If I see that thing, I'ma squash it
///and rip the wings of it off
#pragma GCC optimize ("O3")
#include "bits/stdc++.h"
using namespace std;
#define pb push_back
using ll = long long;
const int N = 1e5 + 5, MOD = 1e9 + 7;
int n, a[N];
int bit[N];
vector<int> ord;
void add(int idx){
  for(++idx; idx <= ord.size();idx+=idx&-idx)bit[idx]++;
}
int get(int idx){
  int ret = 0;
  for(++idx;idx;idx-=idx&-idx)ret += bit[idx];
  return ret;
}
int main(){
#ifdef ONLINE_JUDGE
  ios_base::sync_with_stdio(0); cin.tie(0);
#endif // ONLINE_JUDGE

    int n;
    cin >> n;

    vector<pair<int,int>> vp(n);

    for(int i = 0; i < n; i++){
      cin >> vp[i].first >> vp[i].second;
      ord.pb(vp[i].first);
    }
    sort(ord.begin(),ord.end());
    ord.resize(unique(ord.begin(),ord.end()) - ord.begin());
    sort(vp.begin(),vp.end(),[](pair<int,int>a, pair<int,int> b){
          if(a.second==b.second)
            return a.first < b.first;
          return a.second > b.second;
         });
    long long ans = 0;
    set<int>xs;
    for(int i = 0, j = 0; i < n; i = j){
      for(; vp[i].second == vp[j].second; j++){
          int idx = lower_bound(ord.begin(),ord.end(),vp[j].first)-ord.begin();
          if(xs.count(idx) == 0)
          add(idx);
          xs.insert(idx);
          ans += get(idx);
      }
    }

    cout << ans << '\n';

    return 0;
}


///If I see that thing, I'ma squash it
///and rip the wings of it off
#pragma GCC optimize ("O3")
#include "bits/stdc++.h"
using namespace std;
#define pb push_back
using ll = long long;
const int N = 1e5 + 5, MOD = 1e9 + 7;
int n, a[N];
int main(){
#ifdef ONLINE_JUDGE
  ios_base::sync_with_stdio(0); cin.tie(0);
#endif // ONLINE_JUDGE

    int n;
    cin >> n;

    vector<pair<int,int>> vp(n);

    for(int i = 0; i < n; i++){
      cin >> vp[i].first >> vp[i].second;
    }
    sort(vp.begin(),vp.end(),[](pair<int,int>a, pair<int,int> b){
          return a.second > b.second;
         });
    set<int> xs;
    long long ans = 0;
    for(int i = 0, j = 0; i < n; i = j){
      for(; vp[i].second == vp[j].second; j++){
        xs.insert(vp[j].first);
      }
      ll sz = xs.size();
      ans += (sz * (sz + 1) / 2);
    }

    cout << ans << '\n';

    return 0;
}


///If I see that thing, I'ma squash it
///and rip the wings of it off
#pragma GCC optimize ("O3")
#include "bits/stdc++.h"
using namespace std;
#define pb push_back
using ll = long long;
const int N = 1e5 + 5, MOD = 1e9 + 7;
int n, a[N];
int bit[N];
vector<int> ord;
void add(int idx){
  for(++idx; idx < N;idx+=idx&-idx)bit[idx]++;
}
int get(int idx){
  int ret = 0;
  for(++idx;idx;idx-=idx&-idx)ret += bit[idx];
  return ret;
}
int main(){
#ifdef ONLINE_JUDGE
  ios_base::sync_with_stdio(0); cin.tie(0);
#endif // ONLINE_JUDGE

    int n;
    cin >> n;

    vector<pair<int,int>> vp(n);

    for(int i = 0; i < n; i++){
      cin >> vp[i].first >> vp[i].second;
      ord.pb(vp[i].first);
    }
    sort(ord.begin(),ord.end());
    ord.resize(unique(ord.begin(),ord.end()) - ord.begin());
    sort(vp.begin(),vp.end(),[](pair<int,int>a, pair<int,int> b){
          if(a.second==b.second)
            return a.first < b.first;
          return a.second > b.second;
         });
    long long ans = 0;
    set<int>xs;
    for(int i = 0, j = 0; i < n; i = j){
        vector<int> go;
      for(; vp[i].second == vp[j].second; j++){
          int idx = lower_bound(ord.begin(),ord.end(),vp[j].first)-ord.begin();
          go.pb(idx);
      }
      for(int j = 0; j < go.size(); j++){
        int nxt = ord.size();
        int idx = go[j];
        if(j != go.size()-1)
          nxt = go[j + 1] - 1;
        if(xs.count(idx) == 0){
          xs.insert(idx);
          add(idx);
        }
        int lft = get(nxt) - get(idx-1);
        int rt = get(idx);
        ans += 1ll * lft * rt;
      }
    }

    cout << ans << '\n';

    return 0;
}


#define LOCAL
#include "bits/stdc++.h"
using namespace std;
#define FAST ios_base::sync_with_stdio(false); cin.tie(0);
#define LLINF ((long long) 1e18)//1234567890987654321
#define INF 1234567890ll
#define pb push_back
#define eb emplace_back
#define ins insert
#define f first
#define s second
#define db 0
#define EPS (1e-7)    //0.0000001 the value
#define PI (acos((ld)-1.0))
#define MAXN (300006)
#define ll long long int
#define ld long double
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());    //can be used by calling rng() or shuffle(A, A+n, rng)
#define FOR(ii, ss, ee) for(ll ii = (ss); ii < (ll)(ee); ++ii)
#define space " "
#define cbr cerr << "hi\n"
#define mmst(x, v) memset((x), v, sizeof ((x)))
#define siz(x) ((ll)x.size())
#define ph push
#define btinpct(x) __builtin_popcountll((x))
#define MSB(bm) ((bm==0)?-1:(63-__builtin_clzll((bm))))
#define all(x) (x).begin(), (x).end()
#define lbd(x, y) lower_bound(all(x), y)
#define ubd(x, y) upper_bound(all(x), y)
typedef pair <ll, ll> pi;
typedef pair <ll, pi> spi;
typedef pair <pi, pi> dpi;
inline ll rand(ll x, ll y) { ++y; return (rng() % (y-x)) + x; } //inclusivesss
string to_string(char c) {string s(1,c);return s;}string to_string(bool b){return (b ? "true" : "false");}template <typename A, typename B>string to_string(pair<A, B> p) {return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";}template <typename A>string to_string(A v) {bool first = true;string res = "{";for (const auto &x : v) {if (!first) {res += ", ";}first = false;res += to_string(x);}res += "}";return res;}void degug_out() { cerr << endl; }template <typename Head, typename... Tail>void degug_out(Head H, Tail... T) {cerr << " " << to_string(H);degug_out(T...);}inline ll gcd(ll a,ll b){if(a>b)swap(a,b);if(a==0)return b;return gcd(b%a,a);}
#ifdef LOCAL
// #define degug(...) cerr << "[" << #__VA_ARGS__ << "]:", degug_out(__VA_ARGS__)
#else
// #define degug(...) 663
#define cerr if(0)cout
#endif
ll n;
pi A[MAXN];
int main()
{
	FAST
	cin>>n;
	FOR(i,0,n){
		cin>>A[i].s>>A[i].f;
	}
	sort(A,A+n,greater<pi>());
	set<ll>o; ll co=0; ll ans=0;
	FOR(i,0,n){
		ll inter=0;set<ll>N;while(co<n&&A[co].f==A[i].f) {
			N.ins(A[co].s); if(o.count(A[co].s))inter++;++co;
		}
		ans+=(siz(N)*(siz(o)-inter));
		ans+=(siz(N)*(siz(N)+1)/2);
		i=co-1;
	}
	cout<<ans<<'\n';
}


#define LOCAL
#include "bits/stdc++.h"
using namespace std;
#define FAST ios_base::sync_with_stdio(false); cin.tie(0);
#define LLINF ((long long) 1e18)//1234567890987654321
#define INF 1234567890ll
#define pb push_back
#define eb emplace_back
#define ins insert
#define f first
#define s second
#define db 0
#define EPS (1e-7)    //0.0000001 the value
#define PI (acos((ld)-1.0))
#define MAXN (300006)
#define ll long long int
#define ld long double
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());    //can be used by calling rng() or shuffle(A, A+n, rng)
#define FOR(ii, ss, ee) for(ll ii = (ss); ii < (ll)(ee); ++ii)
#define space " "
#define cbr cerr << "hi\n"
#define mmst(x, v) memset((x), v, sizeof ((x)))
#define siz(x) ((ll)x.size())
#define ph push
#define btinpct(x) __builtin_popcountll((x))
#define MSB(bm) ((bm==0)?-1:(63-__builtin_clzll((bm))))
#define all(x) (x).begin(), (x).end()
#define lbd(x, y) lower_bound(all(x), y)
#define ubd(x, y) upper_bound(all(x), y)
typedef pair <ll, ll> pi;
typedef pair <ll, pi> spi;
typedef pair <pi, pi> dpi;
inline ll rand(ll x, ll y) { ++y; return (rng() % (y-x)) + x; } //inclusivesss
string to_string(char c) {string s(1,c);return s;}string to_string(bool b){return (b ? "true" : "false");}template <typename A, typename B>string to_string(pair<A, B> p) {return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";}template <typename A>string to_string(A v) {bool first = true;string res = "{";for (const auto &x : v) {if (!first) {res += ", ";}first = false;res += to_string(x);}res += "}";return res;}void degug_out() { cerr << endl; }template <typename Head, typename... Tail>void degug_out(Head H, Tail... T) {cerr << " " << to_string(H);degug_out(T...);}inline ll gcd(ll a,ll b){if(a>b)swap(a,b);if(a==0)return b;return gcd(b%a,a);}
#ifdef LOCAL
// #define degug(...) cerr << "[" << #__VA_ARGS__ << "]:", degug_out(__VA_ARGS__)
#else
// #define degug(...) 663
#define cerr if(0)cout
#endif
ll n;
pi A[MAXN];
vector<ll>d;
ll ind(ll x) { return lbd(d,x)-d.begin()+1; }
struct fen {
	ll n,fw[MAXN];
	void init() {
		mmst(fw,0);
		n=siz(d)+2;
	}
	void update(ll x,ll nv){
		for(;x<=n;x+=x&(-x)) fw[x]+=nv;
	}
	ll sum(ll x) {
		ll res=0;
		for(;x;x-=x&(-x)) res+=fw[x];
		return res;
	}
	ll backs(ll a){ return sum(n)-sum(a-1); }
	ll sum(ll a,ll b){if(a<=b)return sum(b)-sum(a-1);else return 0;}
} fen;
ll nC2(ll n) { return n*(n+1)/2; }
int main()
{
	FAST
	cin>>n;
	FOR(i,0,n){
		cin>>A[i].s>>A[i].f; d.pb(A[i].s);
	} sort(all(d)); d.resize(unique(all(d))-d.begin()); fen.init();
	sort(A,A+n,greater<pi>());
	set<ll>o; ll co=0; ll ans=0;
	FOR(i,0,n){
		ll inter=0;vector<ll>N;stack<ll>hold;
		while(co<n&&A[co].f==A[i].f) {
			N.pb(A[co].s); if(o.count(A[co].s))inter++; else hold.emplace(ind(A[co].s)); ++co;
		}
		sort(all(N));
		ll less=fen.sum(ind(*N.begin()) - 1);
		ll more=fen.backs(ind(*--N.end())+1);
		ans+=(siz(N)*(siz(o)-inter));
		ans+=nC2(siz(N));
		vector<ll>v;
		v.pb(less);
		for(ll i=1;i<N.size();++i){
			v.pb(fen.sum(lbd(d,N[i-1]+1)-d.begin()+1,ubd(d,N[i]-1)-d.begin()-1+1)); assert(v.back()>=0); if(v.back()==0)v.pop_back();
		}v.pb(more);
		ll sum=accumulate(all(v),0ll);
		for(auto i:v){
			sum-=i;
			ans+=i*sum;
		}
		i=co-1;while(hold.size()) fen.update(hold.top(),1),hold.pop(); for(auto i:N) o.ins(i);
	}
	cout<<ans<<'\n';
}


#define LOCAL
#include "bits/stdc++.h"
using namespace std;
#define FAST ios_base::sync_with_stdio(false); cin.tie(0);
#define LLINF ((long long) 1e18)//1234567890987654321
#define INF 1234567890ll
#define pb push_back
#define eb emplace_back
#define ins insert
#define f first
#define s second
#define db 0
#define EPS (1e-7)    //0.0000001 the value
#define PI (acos((ld)-1.0))
#define MAXN (300006)
#define ll long long int
#define ld long double
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());    //can be used by calling rng() or shuffle(A, A+n, rng)
#define FOR(ii, ss, ee) for(ll ii = (ss); ii < (ll)(ee); ++ii)
#define space " "
#define cbr cerr << "hi\n"
#define mmst(x, v) memset((x), v, sizeof ((x)))
#define siz(x) ((ll)x.size())
#define ph push
#define btinpct(x) __builtin_popcountll((x))
#define MSB(bm) ((bm==0)?-1:(63-__builtin_clzll((bm))))
#define all(x) (x).begin(), (x).end()
#define lbd(x, y) lower_bound(all(x), y)
#define ubd(x, y) upper_bound(all(x), y)
typedef pair <ll, ll> pi;
typedef pair <ll, pi> spi;
typedef pair <pi, pi> dpi;
inline ll rand(ll x, ll y) { ++y; return (rng() % (y-x)) + x; } //inclusivesss
string to_string(char c) {string s(1,c);return s;}string to_string(bool b){return (b ? "true" : "false");}template <typename A, typename B>string to_string(pair<A, B> p) {return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";}template <typename A>string to_string(A v) {bool first = true;string res = "{";for (const auto &x : v) {if (!first) {res += ", ";}first = false;res += to_string(x);}res += "}";return res;}void degug_out() { cerr << endl; }template <typename Head, typename... Tail>void degug_out(Head H, Tail... T) {cerr << " " << to_string(H);degug_out(T...);}inline ll gcd(ll a,ll b){if(a>b)swap(a,b);if(a==0)return b;return gcd(b%a,a);}
#ifdef LOCAL
// #define degug(...) cerr << "[" << #__VA_ARGS__ << "]:", degug_out(__VA_ARGS__)
#else
// #define degug(...) 663
#define cerr if(0)cout
#endif
ll n;
pi A[MAXN];
vector<ll>d;
ll ind(ll x) { return lbd(d,x)-d.begin()+1; }
struct fen {
	ll n,fw[MAXN];
	void init() {
		mmst(fw,0);
		n=siz(d)+2;
	}
	void update(ll x,ll nv){
		for(;x<=n;x+=x&(-x)) fw[x]+=nv;
	}
	ll sum(ll x) {
		ll res=0;
		for(;x;x-=x&(-x)) res+=fw[x];
		return res;
	}
	ll backs(ll a){ return sum(n)-sum(a-1); }
	ll sum(ll a,ll b){return sum(b)-sum(a-1);}
} fen;
ll nC2(ll n) { return n*(n+1)/2; }
int main()
{
	FAST
	cin>>n;
	FOR(i,0,n){
		cin>>A[i].s>>A[i].f; d.pb(A[i].s);
	} sort(all(d)); d.resize(unique(all(d))-d.begin()); fen.init();
	sort(A,A+n,greater<pi>());
	set<ll>o; ll co=0; ll ans=0;
	FOR(i,0,n){
		ll inter=0;vector<ll>N;stack<ll>hold;
		while(co<n&&A[co].f==A[i].f) {
			N.pb(A[co].s); if(o.count(A[co].s))inter++; else hold.emplace(ind(A[co].s)); ++co;
		}
		sort(all(N));
		ll less=fen.sum(ind(*N.begin()) - 1);
		ll more=fen.backs(ind(*--N.end())+1);
		ans+=(siz(N)*(siz(o)-inter));
		ans+=nC2(siz(N));
		vector<ll>v;
		v.pb(less);
		for(ll i=1;i<N.size();++i){
			v.pb(fen.sum(N[i-1]+1,N[i]-1)); assert(v.back()>=0); if(v.back()==0)v.pop_back();
		}v.pb(more);
		ll sum=accumulate(all(v),0ll);
		for(auto i:v){
			sum-=i;
			ans+=i*sum;
		}
		i=co-1;while(hold.size()) fen.update(hold.top(),1),hold.pop(); for(auto i:N) o.ins(i);
	}
	cout<<ans<<'\n';
}


/*
 * Author : Andrew J
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) x.begin() , x.end()
#define rall(x) x.rbegin() , x.rend()
#define vz(x) x.size()
using ll = long long;
using ii = pair <int , int>;
void in(int &x);
const int N = 2e5 + 10;
const int INF = 1e9 + 50;
const int MOD = 1e9 + 7;
int n;
vector <int> compressedY;
map < int , vector <int> > keepCoor;

ll ways(ll x) {
  return (x * (x + 1)) / 2;
}

void inputAndInit() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);

  cin >> n;

  set <int> distinctY;
  for (int i = 0 ; i < n ; i++) {
    int x, y; cin >> x >> y;
    keepCoor[y].pb(x);
    distinctY.insert(y);
  }

  for (int y : distinctY) {
    compressedY.pb(y);
  }

  return;
}

int main() {

  inputAndInit();

  sort(rall(compressedY));

  set <int> xPoints;

  ll ans = 0;
  for (int y : compressedY) {

    ll other = xPoints.size();

    set <int> curPoints = xPoints;

    for (int x : keepCoor[y]) {
      if (curPoints.find(x) != curPoints.end()) {
        other--;
        curPoints.erase(x);
      }
    }

    ans += ways(keepCoor[y].size());
    ans += other * keepCoor[y].size();

    ll lstLeft = 0, lstRight = 0;
    for (int x : keepCoor[y]) {
      ll left = 0, right = 0;
      for (int otherX : curPoints) {
        if (otherX < x) left++;
        if (otherX > x) right++;
      }
      ans += (left - lstLeft) * right;
      lstLeft = left, lstRight = right;
    }

    for (int x : keepCoor[y]) {
      xPoints.insert(x);
    }
  }

  cout << ans << '\n';
  return 0;
}

void in(int &x){
  bool neg = false;
  register int c;
  x = 0;
  c = getchar();
  if(c == '-'){
    neg = true;
    c = getchar();
  }
  for (; c > 47 && c < 58; c = getchar()) {
    x = (x << 1) + (x << 3) + c - 48;
  }
  if (neg) x *= -1;
}


/*
 * Author : Andrew J
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) x.begin() , x.end()
#define rall(x) x.rbegin() , x.rend()
#define vz(x) x.size()
using ll = long long;
using ii = pair <ll , ll>;
const ll N = 2e5 + 10;
const ll INF = 1e9 + 50;
const ll MOD = 1e9 + 7;
ll n;
ll tree[N * 4];
vector <ll> compressedY, compressedX;
map < ll , vector <ll> > keepCoor;

inline ll ways(ll x) {
  return (x * (x + 1)) / 2;
}

inline ll getIdx(ll x) {
  return (ll)(lower_bound(all(compressedX) , x) - compressedX.begin());
}

void inputAndInit() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);

  cin >> n;

  set <ll> distinctY, distinctX;
  for (ll i = 0 ; i < n ; i++) {
    ll x, y; cin >> x >> y;
    keepCoor[y].pb(x);
    distinctY.insert(y);
    distinctX.insert(x);
  }

  for (ll y : distinctY) {
    compressedY.pb(y);
  }

  for (ll x : distinctX) {
    compressedX.pb(x);
  }

  return;
}

inline ll rangeSum(ll idx , ll l , ll r , ll from , ll to) {
  if (r < from || l > to) return 0;
  if (from <= l && r <= to) return tree[idx];
  ll mid = l + r >> 1;
  return (
    rangeSum(idx << 1 , l , mid , from , to) +
    rangeSum(idx << 1 | 1 , mid + 1 , r , from , to)
  );
}

inline void update(ll idx , ll l , ll r , ll where , ll value) {
  if (r < where || l > where) return;
  if (l == r && l == where) {
    tree[idx] = value;
    return;
  }
  ll mid = l + r >> 1;
  update(idx << 1 , l , mid , where , value);
  update(idx << 1 | 1 , mid + 1 , r , where , value);
  tree[idx] = tree[idx << 1] + tree[idx << 1 | 1];
}

int main() {

  inputAndInit();

  sort(rall(compressedY));

  for (ll y : compressedY) {
    sort(all(keepCoor[y]));
  }

  set <ll> xPoints;

  ll k = compressedX.size();

  ll ans = 0;
  for (ll y : compressedY) {
    ll other = xPoints.size();

    for (ll x : keepCoor[y]) {
      if (xPoints.find(x) != xPoints.end()) {
        update(1 , 0 , k - 1 , getIdx(x) , 0);
        other--;
      }
    }

    ans += ways(keepCoor[y].size());
    ans += other * keepCoor[y].size();

    ll lstLeft = 0;
    for (ll x : keepCoor[y]) {
      ll left = rangeSum(1 , 0 , k - 1 , 0 , getIdx(x) - 1);
      ll right = rangeSum(1 , 0 , k - 1 , getIdx(x) + 1 , k - 1);
      ans += (left - lstLeft) * right;
      lstLeft = left;
    }

    for (ll x : keepCoor[y]) {
      xPoints.insert(x);
      update(1 , 0 , k - 1 , getIdx(x) , 1);
    }
  }

  cout << ans << '\n';
  return 0;
}


/*
 * Author : Andrew J
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) x.begin() , x.end()
#define rall(x) x.rbegin() , x.rend()
#define vz(x) x.size()
using ll = long long;
using ii = pair <int , int>;
void in(int &x);
const int N = 2e5 + 10;
const int INF = 1e9 + 50;
const int MOD = 1e9 + 7;
int n;
int tree[N * 4];
vector <int> compressedY, compressedX;
map < int , vector <int> > keepCoor;

ll ways(ll x) {
  return (x * (x + 1)) / 2;
}

int getIdx(int x) {
  return (int)(lower_bound(all(compressedX) , x) - compressedX.begin());
}

void inputAndInit() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);

  cin >> n;

  set <int> distinctY, distinctX;
  for (int i = 0 ; i < n ; i++) {
    int x, y; cin >> x >> y;
    keepCoor[y].pb(x);
    distinctY.insert(y);
    distinctX.insert(x);
  }

  for (int y : distinctY) {
    compressedY.pb(y);
  }

  for (int x : distinctX) {
    compressedX.pb(x);
  }

  return;
}

int rangeSum(int idx , int l , int r , int from , int to) {
  if (r < from || l > to) return 0;
  if (from <= l && r <= to) return tree[idx];
  int mid = l + r >> 1;
  return (
    rangeSum(idx << 1 , l , mid , from , to) +
    rangeSum(idx << 1 | 1 , mid + 1 , r , from , to)
  );
}

void update(int idx , int l , int r , int where , int value) {
  if (r < where || l > where) return;
  if (l == r && l == where) {
    tree[idx] = value;
    return;
  }
  int mid = l + r >> 1;
  update(idx << 1 , l , mid , where , value);
  update(idx << 1 | 1 , mid + 1 , r , where , value);
  tree[idx] = tree[idx << 1] + tree[idx << 1 | 1];
}

int main() {

  inputAndInit();

  sort(rall(compressedY));

  set <int> xPoints;

  int k = compressedX.size();

  ll ans = 0;
  for (int y : compressedY) {
    sort(all(keepCoor[y]));

    ll other = xPoints.size();

    set <int> curPoints = xPoints;

    for (int x : keepCoor[y]) {
      if (curPoints.find(x) != curPoints.end()) {
        update(1 , 0 , k - 1 , getIdx(x) , 0);
        other--;
        curPoints.erase(x);
      }
    }

    ans += ways(keepCoor[y].size());
    ans += other * keepCoor[y].size();

    ll lstLeft = 0;
    for (int x : keepCoor[y]) {
      ll left = rangeSum(1 , 0 , k - 1 , 0 , getIdx(x) - 1);
      ll right = rangeSum(1 , 0 , k - 1 , getIdx(x) + 1 , k - 1);
      ans += (left - lstLeft) * right;
      lstLeft = left;
    }

    for (int x : keepCoor[y]) {
      xPoints.insert(x);
      update(1 , 0 , k - 1 , getIdx(x) , 1);
    }
  }

  cout << ans << '\n';
  return 0;
}

void in(int &x){
  bool neg = false;
  register int c;
  x = 0;
  c = getchar();
  if(c == '-'){
    neg = true;
    c = getchar();
  }
  for (; c > 47 && c < 58; c = getchar()) {
    x = (x << 1) + (x << 3) + c - 48;
  }
  if (neg) x *= -1;
}


/*
 * Author : Andrew J
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) x.begin() , x.end()
#define rall(x) x.rbegin() , x.rend()
#define vz(x) x.size()
using ll = long long;
using ii = pair <int , int>;
void in(int &x);
const int N = 2e5 + 10;
const int INF = 1e9 + 50;
const int MOD = 1e9 + 7;
int n;
vector <int> compressedY;
map < int , vector <int> > keepCoor;

ll ways(ll x) {
  return (x * (x + 1)) / 2;
}

void inputAndInit() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);

  cin >> n;

  set <int> distinctY;
  for (int i = 0 ; i < n ; i++) {
    int x, y; cin >> x >> y;
    keepCoor[y].pb(x);
    distinctY.insert(y);
  }

  for (int y : distinctY) {
    compressedY.pb(y);
  }

  return;
}

int main() {

  inputAndInit();

  sort(rall(compressedY));

  set <int> xPoints;

  ll ans = 0;
  for (int y : compressedY) {
    sort(all(keepCoor[y]));

    ll other = xPoints.size();

    set <int> curPoints = xPoints;

    for (int x : keepCoor[y]) {
      if (curPoints.find(x) != curPoints.end()) {
        other--;
        curPoints.erase(x);
      }
    }

    ans += ways(keepCoor[y].size());
    ans += other * keepCoor[y].size();

    ll lstLeft = 0;
    for (int x : keepCoor[y]) {
      ll left = 0, right = 0;
      for (int otherX : curPoints) {
        if (otherX < x) left++;
        if (otherX > x) right++;
      }
      ans += (left - lstLeft) * right;
      lstLeft = left;
    }

    for (int x : keepCoor[y]) {
      xPoints.insert(x);
    }
  }

  cout << ans << '\n';
  return 0;
}

void in(int &x){
  bool neg = false;
  register int c;
  x = 0;
  c = getchar();
  if(c == '-'){
    neg = true;
    c = getchar();
  }
  for (; c > 47 && c < 58; c = getchar()) {
    x = (x << 1) + (x << 3) + c - 48;
  }
  if (neg) x *= -1;
}


/*
 * Author : Andrew J
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) x.begin() , x.end()
#define rall(x) x.rbegin() , x.rend()
#define vz(x) x.size()
using ll = long long;
using ii = pair <ll , ll>;
const ll N = 2e5 + 10;
const ll INF = 1e9 + 50;
const ll MOD = 1e9 + 7;
ll n;
ll tree[N * 4];
vector <ll> compressedY, compressedX;
map < ll , vector <ll> > keepCoor;

inline ll ways(ll x) {
  return (x * (x + 1)) / 2;
}

inline ll getIdx(ll x) {
  return (ll)(lower_bound(all(compressedX) , x) - compressedX.begin());
}

void inputAndInit() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);

  cin >> n;

  set <ll> distinctY, distinctX;
  for (ll i = 0 ; i < n ; i++) {
    ll x, y; cin >> x >> y;
    keepCoor[y].pb(x);
    distinctY.insert(y);
    distinctX.insert(x);
  }

  for (ll y : distinctY) {
    compressedY.pb(y);
  }

  for (ll x : distinctX) {
    compressedX.pb(x);
  }

  return;
}

inline ll rangeSum(ll idx , ll l , ll r , ll from , ll to) {
  if (r < from || l > to) return 0;
  if (from <= l && r <= to) return tree[idx];
  ll mid = l + r >> 1;
  return (
    rangeSum(idx << 1 , l , mid , from , to) +
    rangeSum(idx << 1 | 1 , mid + 1 , r , from , to)
  );
}

inline void update(ll idx , ll l , ll r , ll where , ll value) {
  if (r < where || l > where) return;
  if (l == r && l == where) {
    tree[idx] = value;
    return;
  }
  ll mid = l + r >> 1;
  update(idx << 1 , l , mid , where , value);
  update(idx << 1 | 1 , mid + 1 , r , where , value);
  tree[idx] = tree[idx << 1] + tree[idx << 1 | 1];
}

int main() {

  inputAndInit();

  sort(rall(compressedY));

  for (ll y : compressedY) {
    sort(all(keepCoor[y]));
  }

  set <ll> xPoints;

  ll k = compressedX.size();

  ll ans = 0;
  for (ll y : compressedY) {
    ll other = xPoints.size();

    set <ll> curPoints = xPoints;

    for (ll x : keepCoor[y]) {
      if (curPoints.find(x) != curPoints.end()) {
        update(1 , 0 , k - 1 , getIdx(x) , 0);
        other--;
        curPoints.erase(x);
      }
    }

    ans += ways(keepCoor[y].size());
    ans += other * keepCoor[y].size();

    ll lstLeft = 0;
    for (ll x : keepCoor[y]) {
      ll left = rangeSum(1 , 0 , k - 1 , 0 , getIdx(x) - 1);
      ll right = rangeSum(1 , 0 , k - 1 , getIdx(x) + 1 , k - 1);
      ans += (left - lstLeft) * right;
      lstLeft = left;
    }

    for (ll x : keepCoor[y]) {
      xPoints.insert(x);
      update(1 , 0 , k - 1 , getIdx(x) , 1);
    }
  }

  cout << ans << '\n';
  return 0;
}


/*
 * Author : Andrew J
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) x.begin() , x.end()
#define rall(x) x.rbegin() , x.rend()
#define vz(x) x.size()
using ll = long long;
using ii = pair <ll , ll>;
const ll N = 2e5 + 10;
const ll INF = 1e9 + 50;
const ll MOD = 1e9 + 7;
ll n;
ll tree[N * 4];
vector <ll> compressedY, compressedX;
map < ll , vector <ll> > keepCoor;

ll ways(ll x) {
  return (x * (x + 1)) / 2;
}

ll getIdx(ll x) {
  return (ll)(lower_bound(all(compressedX) , x) - compressedX.begin());
}

void inputAndInit() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);

  cin >> n;

  set <ll> distinctY, distinctX;
  for (ll i = 0 ; i < n ; i++) {
    ll x, y; cin >> x >> y;
    keepCoor[y].pb(x);
    distinctY.insert(y);
    distinctX.insert(x);
  }

  for (ll y : distinctY) {
    compressedY.pb(y);
  }

  for (ll x : distinctX) {
    compressedX.pb(x);
  }

  return;
}

ll rangeSum(ll idx , ll l , ll r , ll from , ll to) {
  if (r < from || l > to) return 0;
  if (from <= l && r <= to) return tree[idx];
  ll mid = l + r >> 1;
  return (
    rangeSum(idx << 1 , l , mid , from , to) +
    rangeSum(idx << 1 | 1 , mid + 1 , r , from , to)
  );
}

void update(ll idx , ll l , ll r , ll where , ll value) {
  if (r < where || l > where) return;
  if (l == r && l == where) {
    tree[idx] = value;
    return;
  }
  ll mid = l + r >> 1;
  update(idx << 1 , l , mid , where , value);
  update(idx << 1 | 1 , mid + 1 , r , where , value);
  tree[idx] = tree[idx << 1] + tree[idx << 1 | 1];
}

int main() {

  inputAndInit();

  sort(rall(compressedY));

  for (ll y : compressedY) {
    sort(all(keepCoor[y]));
  }

  set <ll> xPoints;

  ll k = compressedX.size();

  ll ans = 0;
  for (ll y : compressedY) {
    ll other = xPoints.size();

    set <ll> curPoints = xPoints;

    for (ll x : keepCoor[y]) {
      if (curPoints.find(x) != curPoints.end()) {
        update(1 , 0 , k - 1 , getIdx(x) , 0);
        other--;
        curPoints.erase(x);
      }
    }

    ans += ways(keepCoor[y].size());
    ans += other * keepCoor[y].size();

    ll lstLeft = 0;
    for (ll x : keepCoor[y]) {
      ll left = rangeSum(1 , 0 , k - 1 , 0 , getIdx(x) - 1);
      ll right = rangeSum(1 , 0 , k - 1 , getIdx(x) + 1 , k - 1);
      ans += (left - lstLeft) * right;
      lstLeft = left;
    }

    for (ll x : keepCoor[y]) {
      xPoints.insert(x);
      update(1 , 0 , k - 1 , getIdx(x) , 1);
    }
  }

  cout << ans << '\n';
  return 0;
}


/*
 * Author : Andrew J
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) x.begin() , x.end()
#define rall(x) x.rbegin() , x.rend()
#define vz(x) x.size()
using ll = long long;
using ii = pair <int , int>;
void in(int &x);
const int N = 2e5 + 10;
const int INF = 1e9 + 50;
const int MOD = 1e9 + 7;
int n;
vector <int> compressedY;
map < int , vector <int> > keepCoor;

ll ways(ll x) {
  return (x * (x + 1)) / 2;
}

void inputAndInit() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);

  cin >> n;

  set <int> distinctY;
  for (int i = 0 ; i < n ; i++) {
    int x, y; cin >> x >> y;
    keepCoor[y].pb(x);
    distinctY.insert(y);
  }

  for (int y : distinctY) {
    compressedY.pb(y);
  }

  return;
}

int main() {

  inputAndInit();

  sort(rall(compressedY));

  set <int> xPoints;

  ll ans = 0;
  for (int y : compressedY) {

    ll other = xPoints.size();

    set <int> curPoints = xPoints;

    for (int x : keepCoor[y]) {
      if (curPoints.find(x) != curPoints.end()) {
        other--;
        curPoints.erase(x);
      }
    }

    ans += ways(keepCoor[y].size());
    ans += other * keepCoor[y].size();

    ll lstLeft = -1, lstRight = -1;
    for (int x : keepCoor[y]) {
      ll left = 0, right = 0;
      for (int otherX : curPoints) {
        if (otherX < x) left++;
        if (otherX > x) right++;
      }
      if (left != lstLeft || right != lstRight) {
        ans += left * right;
      }
      lstLeft = left, lstRight = right;
    }

    for (int x : keepCoor[y]) {
      xPoints.insert(x);
    }
  }

  cout << ans << '\n';
  return 0;
}

void in(int &x){
  bool neg = false;
  register int c;
  x = 0;
  c = getchar();
  if(c == '-'){
    neg = true;
    c = getchar();
  }
  for (; c > 47 && c < 58; c = getchar()) {
    x = (x << 1) + (x << 3) + c - 48;
  }
  if (neg) x *= -1;
}


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <utility>
#include <string>
#include <algorithm>
#include <map>
#include <vector>
#include <climits>
#include <numeric>
#include<stdio.h>
#include <queue>
#include <iomanip>
#include <float.h>
#include <set>
#include<functional>
#include <stack>
#include <time.h>
#include <climits>
#include <bitset>
#include <fstream>
#include<map>
using namespace std;
long long gcd(long long a, long long b) {
	if (b == 0) return a;
	return gcd(b, a % b);
}
vector<long long> divisor(long long n) {
	vector< long long> ret;
	for (long long i = 1; i * i <= n; i++) {
		if (n % i == 0) {
			ret.push_back(i);
			if (i * i != n) ret.push_back(n / i);
		}
	}
	sort(begin(ret), end(ret));
	return (ret);
}
struct LazySegmentTree {
private:
	int n;
	vector<int> node, lazy;
	vector<bool> lazyFlag;

public:
	LazySegmentTree(vector<int> v) {
		int sz = (int)v.size();
		n = 1; while (n < sz) n *= 2;
		node.resize(2 * n - 1);
		lazy.resize(2 * n - 1, INT_MAX);
		lazyFlag.resize(2 * n - 1, false);

		for (int i = 0; i<sz; i++) node[i + n - 1] = v[i];
		for (int i = n - 2; i >= 0; i--) node[i] = min(node[i * 2 + 1], node[i * 2 + 2]);
	}

	void lazyEvaluate(int k, int l, int r) {
		if (lazyFlag[k]) {
			node[k] = lazy[k];
			if (r - l > 1) {
				lazy[k * 2 + 1] = lazy[k * 2 + 2] = lazy[k];
				lazyFlag[k * 2 + 1] = lazyFlag[k * 2 + 2] = true;
			}
			lazyFlag[k] = false;
		}
	}

	void update(int a, int b, int x, int k = 0, int l = 0, int r = -1) {
		if (r < 0) r = n;
		lazyEvaluate(k, l, r);
		if (b <= l || r <= a) return;
		if (a <= l && r <= b) {
			lazy[k] = x;
			lazyFlag[k] = true;
			lazyEvaluate(k, l, r);
		}
		else {
			update(a, b, x, 2 * k + 1, l, (l + r) / 2);
			update(a, b, x, 2 * k + 2, (l + r) / 2, r);
			node[k] = min(node[2 * k + 1], node[2 * k + 2]);
		}
	}

	int find(int a, int b, int k = 0, int l = 0, int r = -1) {
		if (r < 0) r = n;
		lazyEvaluate(k, l, r);
		if (b <= l || r <= a) return INT_MAX;
		if (a <= l && r <= b) return node[k];
		int vl = find(a, b, 2 * k + 1, l, (l + r) / 2);
		int vr = find(a, b, 2 * k + 2, (l + r) / 2, r);
		return min(vl, vr);
	}
};
#define ll long long
class UnionFind {
public:
	vector <ll> par; // 各元の親を表す配列
	vector <ll> siz; // 素集合のサイズを表す配列(1 で初期化)

					 // Constructor
	UnionFind(ll sz_) : par(sz_), siz(sz_, 1LL) {
		for (ll i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身
	}
	void init(ll sz_) {
		par.resize(sz_);
		siz.assign(sz_, 1LL);  // resize だとなぜか初期化されなかった
		for (ll i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身
	}

	// Member Function
	// Find
	ll root(ll x) { // 根の検索
		while (par[x] != x) {
			x = par[x] = par[par[x]]; // x の親の親を x の親とする
		}
		return x;
	}

	// Union(Unite, Merge)
	bool merge(ll x, ll y) {
		x = root(x);
		y = root(y);
		if (x == y) return false;
		// merge technique（データ構造をマージするテク．小を大にくっつける）
		if (siz[x] < siz[y]) swap(x, y);
		siz[x] += siz[y];
		par[y] = x;
		return true;
	}

	bool issame(ll x, ll y) { // 連結判定
		return root(x) == root(y);
	}

	ll size(ll x) { // 素集合のサイズ
		return siz[root(x)];
	}
};
typedef pair<long long,long long>p;
long long mod = 1000000007;
long long calc(long long k) {
	return k*(k + 1) / 2;
}
class BIT
{
public:
	vector<int> bit;
	int M;

	BIT(int M) :
		bit(vector<int>(M + 1, 0)), M(M) {}

	int sum(int i) {
		if (!i) return 0;
		if (i == 0)return 0;
		return bit[i] + sum(i - (i&-i));
	}

	void add(int i, int x) {
		if (i > M) return;
		bit[i] += x;
		add(i + (i&-i), x);
	}
};
signed main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
	cout << fixed << setprecision(16);
	int n;
	cin >> n;
	map<long long,long long>ma;
	p point[200004];//y,x
	vector<int>xs;
	for (int i = 0; i < n; i++) {
		cin >> point[i].second >> point[i].first;
		xs.push_back(point[i].second);
	}
	sort(point, point + n);
	sort(xs.begin(), xs.end());
	map<long long, long long>ma2;//x座標が何番目か
	int tt = 1;
	for (int i = 0; i < n; i++) {
		if (ma2.find(xs[i]) == ma2.end()) {
			ma2[xs[i]] = tt++;
		}
	}
	long long ans = 0;
	long long cnt = 0;
	int now = n-1;
	BIT bi(n);
	while (1) {
		if (now == -1)break;
		vector<int>ne;
		int index = ma2[point[now].second];
		ans -= calc(bi.sum(n)-bi.sum(index));
		if (ma.find(point[now].second) == ma.end()) { cnt += 1; ma[point[now].second] = 1; ne.push_back(point[now].second); }
		while (now -1>=0&&point[now].first == point[now - 1].first) {
			now -= 1;
			if (ma.find(point[now].second) == ma.end()) {
				cnt += 1; ma[point[now].second] = 1; ne.push_back(point[now].second);
			}
			int index2 = ma2[point[now].second];
			if (index-1 > index2) {
				ans -= calc(bi.sum(index -1) - bi.sum(index2));
			}
			index = index2;
		}
		ans -= calc(bi.sum(index - 1));
		ans+=calc(cnt);
		for (int i = 0; i < ne.size(); i++) {
			bi.add(ma2[point[now].second], 1);
		}
		now -= 1;
	}
	cout << ans;
}


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <utility>
#include <string>
#include <algorithm>
#include <map>
#include <vector>
#include <climits>
#include <numeric>
#include<stdio.h>
#include <queue>
#include <iomanip>
#include <float.h>
#include <set>
#include<functional>
#include <stack>
#include <time.h>
#include <climits>
#include <bitset>
#include <fstream>
#include<map>
using namespace std;
long long gcd(long long a, long long b) {
	if (b == 0) return a;
	return gcd(b, a % b);
}
vector<long long> divisor(long long n) {
	vector< long long> ret;
	for (long long i = 1; i * i <= n; i++) {
		if (n % i == 0) {
			ret.push_back(i);
			if (i * i != n) ret.push_back(n / i);
		}
	}
	sort(begin(ret), end(ret));
	return (ret);
}
struct LazySegmentTree {
private:
	int n;
	vector<int> node, lazy;
	vector<bool> lazyFlag;

public:
	LazySegmentTree(vector<int> v) {
		int sz = (int)v.size();
		n = 1; while (n < sz) n *= 2;
		node.resize(2 * n - 1);
		lazy.resize(2 * n - 1, INT_MAX);
		lazyFlag.resize(2 * n - 1, false);

		for (int i = 0; i<sz; i++) node[i + n - 1] = v[i];
		for (int i = n - 2; i >= 0; i--) node[i] = min(node[i * 2 + 1], node[i * 2 + 2]);
	}

	void lazyEvaluate(int k, int l, int r) {
		if (lazyFlag[k]) {
			node[k] = lazy[k];
			if (r - l > 1) {
				lazy[k * 2 + 1] = lazy[k * 2 + 2] = lazy[k];
				lazyFlag[k * 2 + 1] = lazyFlag[k * 2 + 2] = true;
			}
			lazyFlag[k] = false;
		}
	}

	void update(int a, int b, int x, int k = 0, int l = 0, int r = -1) {
		if (r < 0) r = n;
		lazyEvaluate(k, l, r);
		if (b <= l || r <= a) return;
		if (a <= l && r <= b) {
			lazy[k] = x;
			lazyFlag[k] = true;
			lazyEvaluate(k, l, r);
		}
		else {
			update(a, b, x, 2 * k + 1, l, (l + r) / 2);
			update(a, b, x, 2 * k + 2, (l + r) / 2, r);
			node[k] = min(node[2 * k + 1], node[2 * k + 2]);
		}
	}

	int find(int a, int b, int k = 0, int l = 0, int r = -1) {
		if (r < 0) r = n;
		lazyEvaluate(k, l, r);
		if (b <= l || r <= a) return INT_MAX;
		if (a <= l && r <= b) return node[k];
		int vl = find(a, b, 2 * k + 1, l, (l + r) / 2);
		int vr = find(a, b, 2 * k + 2, (l + r) / 2, r);
		return min(vl, vr);
	}
};
#define ll long long
class UnionFind {
public:
	vector <ll> par; // 各元の親を表す配列
	vector <ll> siz; // 素集合のサイズを表す配列(1 で初期化)

					 // Constructor
	UnionFind(ll sz_) : par(sz_), siz(sz_, 1LL) {
		for (ll i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身
	}
	void init(ll sz_) {
		par.resize(sz_);
		siz.assign(sz_, 1LL);  // resize だとなぜか初期化されなかった
		for (ll i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身
	}

	// Member Function
	// Find
	ll root(ll x) { // 根の検索
		while (par[x] != x) {
			x = par[x] = par[par[x]]; // x の親の親を x の親とする
		}
		return x;
	}

	// Union(Unite, Merge)
	bool merge(ll x, ll y) {
		x = root(x);
		y = root(y);
		if (x == y) return false;
		// merge technique（データ構造をマージするテク．小を大にくっつける）
		if (siz[x] < siz[y]) swap(x, y);
		siz[x] += siz[y];
		par[y] = x;
		return true;
	}

	bool issame(ll x, ll y) { // 連結判定
		return root(x) == root(y);
	}

	ll size(ll x) { // 素集合のサイズ
		return siz[root(x)];
	}
};
typedef pair<long long,long long>p;
long long mod = 1000000007;
long long calc(long long k) {
	return k*(k + 1) / 2;
}
signed main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
	cout << fixed << setprecision(16);
	int n;
	cin >> n;
	map<long long,long long>ma;
	p point[200004];
	long long cnt = 0;
	for (int i = 0; i < n; i++) {
		cin >> point[i].second >> point[i].first;//y,x
		if (ma.find(point[i].second) == ma.end()) { ma[point[i].second] = 1; cnt += 1; }
		else ma[point[i].second] += 1;
	}
	sort(point, point + n);
	long long ans = 0;
	ans += calc(cnt);
	int now = 0;
	while (1) {
		if (now == n)break;
		ma[point[now].second] -= 1;
		if (ma[point[now].second] == 0)cnt -= 1;
		while (now + 1<n&&point[now].first == point[now + 1].first) {
			now += 1;
			ma[point[now].second] -= 1;
			if (ma[point[now].second] == 0)cnt -= 1;
		}
		ans+=calc(cnt);
		now += 1;
	}
	cout << ans;
}


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <utility>
#include <string>
#include <algorithm>
#include <map>
#include <vector>
#include <climits>
#include <numeric>
#include<stdio.h>
#include <queue>
#include <iomanip>
#include <float.h>
#include <set>
#include<functional>
#include <stack>
#include <time.h>
#include <climits>
#include <bitset>
#include <fstream>
#include<map>
using namespace std;
long long gcd(long long a, long long b) {
	if (b == 0) return a;
	return gcd(b, a % b);
}
vector<long long> divisor(long long n) {
	vector< long long> ret;
	for (long long i = 1; i * i <= n; i++) {
		if (n % i == 0) {
			ret.push_back(i);
			if (i * i != n) ret.push_back(n / i);
		}
	}
	sort(begin(ret), end(ret));
	return (ret);
}
struct LazySegmentTree {
private:
	int n;
	vector<int> node, lazy;
	vector<bool> lazyFlag;

public:
	LazySegmentTree(vector<int> v) {
		int sz = (int)v.size();
		n = 1; while (n < sz) n *= 2;
		node.resize(2 * n - 1);
		lazy.resize(2 * n - 1, INT_MAX);
		lazyFlag.resize(2 * n - 1, false);

		for (int i = 0; i<sz; i++) node[i + n - 1] = v[i];
		for (int i = n - 2; i >= 0; i--) node[i] = min(node[i * 2 + 1], node[i * 2 + 2]);
	}

	void lazyEvaluate(int k, int l, int r) {
		if (lazyFlag[k]) {
			node[k] = lazy[k];
			if (r - l > 1) {
				lazy[k * 2 + 1] = lazy[k * 2 + 2] = lazy[k];
				lazyFlag[k * 2 + 1] = lazyFlag[k * 2 + 2] = true;
			}
			lazyFlag[k] = false;
		}
	}

	void update(int a, int b, int x, int k = 0, int l = 0, int r = -1) {
		if (r < 0) r = n;
		lazyEvaluate(k, l, r);
		if (b <= l || r <= a) return;
		if (a <= l && r <= b) {
			lazy[k] = x;
			lazyFlag[k] = true;
			lazyEvaluate(k, l, r);
		}
		else {
			update(a, b, x, 2 * k + 1, l, (l + r) / 2);
			update(a, b, x, 2 * k + 2, (l + r) / 2, r);
			node[k] = min(node[2 * k + 1], node[2 * k + 2]);
		}
	}

	int find(int a, int b, int k = 0, int l = 0, int r = -1) {
		if (r < 0) r = n;
		lazyEvaluate(k, l, r);
		if (b <= l || r <= a) return INT_MAX;
		if (a <= l && r <= b) return node[k];
		int vl = find(a, b, 2 * k + 1, l, (l + r) / 2);
		int vr = find(a, b, 2 * k + 2, (l + r) / 2, r);
		return min(vl, vr);
	}
};
#define ll long long
class UnionFind {
public:
	vector <ll> par; // 各元の親を表す配列
	vector <ll> siz; // 素集合のサイズを表す配列(1 で初期化)

					 // Constructor
	UnionFind(ll sz_) : par(sz_), siz(sz_, 1LL) {
		for (ll i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身
	}
	void init(ll sz_) {
		par.resize(sz_);
		siz.assign(sz_, 1LL);  // resize だとなぜか初期化されなかった
		for (ll i = 0; i < sz_; ++i) par[i] = i; // 初期では親は自分自身
	}

	// Member Function
	// Find
	ll root(ll x) { // 根の検索
		while (par[x] != x) {
			x = par[x] = par[par[x]]; // x の親の親を x の親とする
		}
		return x;
	}

	// Union(Unite, Merge)
	bool merge(ll x, ll y) {
		x = root(x);
		y = root(y);
		if (x == y) return false;
		// merge technique（データ構造をマージするテク．小を大にくっつける）
		if (siz[x] < siz[y]) swap(x, y);
		siz[x] += siz[y];
		par[y] = x;
		return true;
	}

	bool issame(ll x, ll y) { // 連結判定
		return root(x) == root(y);
	}

	ll size(ll x) { // 素集合のサイズ
		return siz[root(x)];
	}
};
typedef pair<long long, long long>p;
long long mod = 1000000007;
long long calc(long long k) {
	return k*(k + 1) / 2;
}
signed main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
	cout << fixed << setprecision(16);
	int n;
	cin >> n;
	map<int, int>ma;
	p point[200004];
	long long cnt = 0;
	for (int i = 0; i < n; i++) {
		cin >> point[i].second >> point[i].first;//y,x
		if (ma.find(point[i].second) == ma.end()) { ma[point[i].second] = 1; cnt += 1; }
		else ma[point[i].second] += 1;
	}
	sort(point, point + n);
	long long ans = 0;
	ans += calc(cnt);
	int now = 0;
	while (1) {
		if (now == n)break;
		while (now + 1<n&&point[now].first == point[now + 1].first) {
			ma[point[now].second] -= 1;
			if (ma[point[now].second] == 0)cnt -= 1;
			now += 1;
		}
		ma[point[now].second] -= 1;
		if (ma[point[now].second] == 0)cnt -= 1;
		now += 1;
		ans += calc(cnt);
	}
	cout << ans;
}