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 } }
小结:我们并不能枚举一条边然后向一个方向枚举,证明在此省略。
不要忘记如果枚举的两个点坐标相同,那么答案是一定会增加的,所以我们将这样的情况舍去。