Squares poj-2002

    题目大意:在笛卡尔坐标系中给出n个点,求这些点可以构成多少个正方形。

    注释:$1\le n\le 10^3$,$-2\cdot 10^3\le x , y\le 2\cdot 10^3$.

      想法:最基本的办法是n个点中枚举三个点,然后用桶判断第四个点是否存在。然后我们想一想这个方法怎么优化,首先,枚举三个点我们可以进而优化成为枚举一条边,然后判断可能出现的两个正方形是否存在,时间复杂度$O(n^2)$。对于空间复杂度,4000*4000的桶显然开不下,我们自然而然想到hash处理。但是横纵坐标还存在负权值,故此我们期望找到一个即不区分正负,但是用能将点期望离散开的hash计算方式。首先想到|x|+|y|。但是这种方式并不能很好的将所有点都离散开,更容易想到x*x+y*y。上下的就是细节的事情了。

    最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define SIZE 4000010
#define mod 99991//关于hash的mod数
using namespace std;
typedef long long ll;
int head[2*SIZE];
int tot;//总点数
int nxt[2*SIZE];
struct Node
{
	int x;
	int y;
}poi[1001000];//单个的点
Node val[SIZE];//hash中点的横纵坐标
void insert(int a)//插入一个点
{
	int value=(poi[a].x*poi[a].x%mod+poi[a].y*poi[a].y%mod)%mod;
	tot++;
	nxt[tot]=head[value];
	head[value]=tot;
	val[tot].x=poi[a].x;
	val[tot].y=poi[a].y;
}
bool find(int x,int y)//寻找横纵坐标分别为x,y的点是否存在
{
	int value=(x*x%mod+y*y%mod)%mod;
	for(int i=head[value];i;i=nxt[i])
	{
		if(poi[i].x==x&&poi[i].y==y) return true;
	}
	return false;
}
ll ans=0;//统计答案
void original()//初始化
{
	tot=0;
	memset(head,0,sizeof head);
	memset(poi,0,sizeof poi);
	ans=0;
}
int main()
{	
	ans=0;
	int n;
	while(1)
	{
		original();
		scanf("%d",&n);
		if(!n) return 0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d",&poi[i].x,&poi[i].y);
			insert(i);
		}
		int x1=0;int y1=0;
		int x2=0;int y2=0;
		int x3=0;int y3=0;
		int x4=0;int y4=0;
		for(int i=1;i<=n;i++)//枚举两个点,判断是否存在相应的正方形
		{
			x1=poi[i].x;y1=poi[i].y;
			for(int j=i+1;j<=n;j++)//由于我check的方式是采取continue的方式,所以需要写两个
			{						//关于j的for循环
				x2=poi[j].x;y2=poi[j].y;
				if(x1==x2&&y1==y2) continue;
				x3=x1-y2+y1;
				y3=y1+x2-x1;
				if(!find(x3,y3)) continue;
				x4=x2-y2+y1;
				y4=y2+x2-x1;
				if(!find(x4,y4)) continue;
				ans++;
			}
			for(int j=i+1;j<=n;j++)
			{
				x2=poi[j].x;y2=poi[j].y;
				if(x1==x2&&y1==y2) continue;
				x3=x1-y1+y2;
				y3=y1+x1-x2;
				if(!find(x3,y3)) continue;
				x4=x2-y1+y2;
				y4=y2+x1-x2;
				if(!find(x4,y4)) continue;
				ans++;
			}
		}
		printf("%lld\n",ans/4);//不要忘记答案除以4
	}
}

     小结:我们并不能枚举一条边然后向一个方向枚举,证明在此省略。

        不要忘记如果枚举的两个点坐标相同,那么答案是一定会增加的,所以我们将这样的情况舍去。