海上には灯台がたくさんあり、行き交う船を照らします。
(図1)
図1に示すように、各灯台には北東側と南西側の2つの直角エリアを照らすサーチライトが装備されています。サーチライトはあらゆる距離をカバーできるほど強力です。灯台自体は非常に小さいので、お互いを遮ることはないと考えられます。
(図2)
灯台Aと灯台Bがお互いの照射範囲内にある場合、相互に照射できると言われています。たとえば、図 2 の例では、青と赤の灯台は相互に照明できますが、青と緑の灯台は相互に照明できず、赤と緑の灯台も互いに照明できません。
ここで、特定の灯台のセットについて、何組の灯台が相互に照明できるかを計算します。
入力形式:
合計 n+1 行。
最初の行には、灯台の総数を表す整数 n が含まれています。
行 2 から n+1 にはそれぞれ、各灯台の水平座標と垂直座標を表す 2 つの整数 x、y が含まれています。
要件:
1 ≤ n ≤ 4×10^6
灯台の座標 x および y は整数であり、異なる灯台の x および y 座標は互いに異なります
1 ≤ x, y ≤ 10^8
出力形式:
相互に照明できる灯台のペアの数を表す 1 つの整数。
入力サンプル:
3
2 2
4 3
5 1
出力サンプル:
対応する出力をここに示します。例えば:
1
コード:
//本题的逻辑结构:线性
//本题的存储结构:顺序结构
//解题思路和算法:升序快排x坐标,升序归并排序y坐标并求逆序对,答案等于n*(n-1)/2-逆序对数
//效率:时间复杂度O(nlogn)、空间复杂度O(n)
//测试数据:
/*
(1)
输入:
3
2 2
4 3
5 1
输出:
1
*/
#include <bits/stdc++.h>
#define rep(i,a,b) for(auto i=(a);i<=(b);i++)
#define dep(i,a,b) for(auto i=(a);i>=(b);--i)
using namespace std;
typedef long long ll;
const int N = 4e6+10;
ll n;
struct node {
/* 灯塔结构体,存x和y坐标 */
int x,y;
}s[N];
int y[N]; /* 单独存y坐标,用来归并排序 */
int temp[N]; /* 存排序好的y坐标 */
ll cnt; /* 排序前y坐标逆序对数量 */
void MergeArray(int left,int mid,int right); /* 归并排序数组赋值,将当前区间已排序元素复制到temp数组中 */
void MergeSort(int left,int right); /* 归并排序 */
void AC(){
scanf("%lld",&n);
rep(i,1,n){
scanf("%d %d",&s[i].x,&s[i].y);
}
sort(s+1,s+1+n,[&](node a,node b){
return a.x<b.x;}); /* 利用匿名结构体排序函数对灯塔按x坐标升序排序 */
rep(i,1,n){
y[i] = s[i].y; /* 将y坐标另外存到数组中便于归并排序 */
}
MergeSort(1,n); /* 对y进行归并排序并统计逆序对数量 */
printf("%lld\n",n*(n-1)/2-cnt); /* 答案等于次序总数减去逆序对个数(即顺序对个数 */
}
int main(){
int _ = 1;/* 样例组数 */
//scanf("%d",&_);
while(_--){
AC();
}
return 0;
}
void MergeSort(int left,int right){
/* 归并排序 */
if(left >= right)return;
int mid = (left+right)>>1;
MergeSort(left,mid); /* 排序左半部分 */
MergeSort(mid+1,right); /* 排序右半部分 */
MergeArray(left,mid,right); /* 将当前区间已排序元素复制到temp数组中 */
}
void MergeArray(int left,int mid,int right){
/* 将当前区间已排序元素复制到temp数组中 */
int tot = 0; /* temp数组指针 */
int t1 = left; /* 区间左半部分指针 */
int t2 = mid + 1; /* 区间右半部分指针 */
while(t1 <= mid && t2 <= right){
/* 当任意一部分未遍历完 */
if(y[t1] < y[t2]) {
/* 左半部分当前元素小 */
temp[++tot] = y[t1++];
}
else {
/* 右半部分当前元素小 */
temp[++tot] = y[t2++];
cnt += mid-t1+1; /* 统计逆序对数量 */
}
}
while(t1 <= mid) {
/* 遍历左半部分剩下元素 */
temp[++tot] = y[t1++];
}
while(t2 <= right) {
/* 遍历右半部分剩下元素 */
temp[++tot] = y[t2++];
}
rep(i,left,right) {
/* 将已排序好的元素复制到y数组中 */
y[i] = temp[i-left+1];
}
}