Codeforces #345 Div2 C Watchmen 容斥

给你n个点的坐标(x,y)。求哈密顿距离和欧几里德距离相同的点对有多少,所给的点可能是重合的。

哈密顿距离:|xi-xj|+|yi-yj|,欧几里德距离:根号下((xi-xj)^2+(yi-yj)^2)。把两边平方一下,再一消去,就可发现,只有当(xi-xj)和(yi-yj)至少有一个为0时,两种距离才相等,就是两个点的横纵坐标至少有一个是一样的。

所以,因为有重点,就显得有点麻烦,但其实想了想,也不麻烦啊。首先把所有点按x坐标排序,每类所有x坐标相同的点,加入有m个,取C(m,2)。不管纵坐标相不相同,都可以这么取。然后再按y坐标排序,同样对每类所有y坐标相同的点,取个C(m,2)。这时就会发现,那些两两相同的,被计算了两次。所以找到每类x和y都相同的点,也取C(m,2)。把最后这个减去就好了。

代码:

#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 200002
struct node
{
	int x, y;
}a[maxn];
bool cmp1(node a, node b)
{
	if (a.x < b.x)
		return 1;
	else if (a.x == b.x&&a.y < b.y)
		return 1;
	else
		return 0;
}
bool cmp2(node a, node b)
{
	if (a.y < b.y)
		return 1;
	else if (a.y == b.y&&a.x < b.x)
		return 1;
	else
		return 0;
}
int main()
{
	//freopen("input.txt", "r", stdin);
	int n;
	long long cnt = 0, ans = 0, l = 0;
	scanf("%d", &n);
	for (int i = 0; i < n; ++i)
		scanf("%d%d", &a[i].x, &a[i].y);
	sort(a, a + n, cmp1);
	cnt = 1;
	for (int i = 1; i < n; ++i)
	{
		if (a[i].x == a[i - 1].x)
			cnt++;
		else
		{
			ans += cnt*(cnt - 1) / 2;
			cnt = 1;
		}
	}
	ans += cnt*(cnt - 1) / 2;
	cnt = 1;
	sort(a, a + n, cmp2);
	for (int i = 1; i < n; ++i)
	{
		if (a[i].y == a[i - 1].y)
			cnt++;
		else
		{
			ans += cnt*(cnt - 1) / 2;
			cnt = 1;
		}
	}
	ans += cnt*(cnt - 1) / 2;
	cnt = 1;
	for (int i = 1; i < n; ++i)
	{
		if (a[i].y == a[i - 1].y&&a[i].x == a[i - 1].x)
			cnt++;
		else
		{
			ans -= cnt*(cnt - 1) / 2;
			cnt = 1;
		}
	}
	ans -= cnt*(cnt - 1) / 2;
	cnt = 1;
	printf("%I64d\n", ans);
	//while (1);
	//system("pause");
	return 0;
}

你可能感兴趣的:(Codeforces #345 Div2 C Watchmen 容斥)