题目传送门:http://codeforces.com/contest/889/problem/D
题目大意:现在有n个点,求有多少条过原点的直线l,使得这n个点在l上的投影构成的可重点集关于某个点P对称。无数多条则输出-1。
题目分析:设l=kx+b,则很明显这n个点在直线上的投影与b无关。由于投影构成的可重点集的对称中心P必为这n个点的中心在直线上的投影,不妨直接设直线过这n个点的中心 mid(∑xn,∑yn) 。如果有两个点关于mid对称,则无论k为何值,这两个点在直线上的投影都关于mid对称,所以用一个Hash表把这些对称点对删去。
对于剩下的点,如果小于等于1个,即有无穷多解。否则选定一个点x,枚举投影后与x对应的点y是哪个,或者x的投影本身就是中心点mid:
我们算出x与y的中点z,则与z,mid所在直线垂直的直线l即为可以使x,y的投影关于mid对称的直线。其它点w在l上的投影到mid在l上的投影的距离,即为向量(w,z)与向量(w,mid)的叉积除以向量(z,mid)的模长。由于模长是定值,直接算叉积,看有没有另外一个点的叉积与其加起来为0即可。
(由于我没有写过多少次计几,导致我写的代码错漏百出,调完各种bug才AC。错误都用注释写在代码里了。)
话说这题出数据好像很困难啊。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=2010;
const double eps=1e-4; //eps开小一点!
const long long M1=998244353;
const long long M2=1000000007;
const long long M=1333331;
typedef long long LL;
struct Hash_data
{
int vX,vY,id;
} Hash[M];
struct data
{
double X,Y;
} point[maxn];
data mid;
double len[maxn];
bool vis[maxn];
data temp[maxn];
int n;
void Push(LL x,LL y,int Id)
{
LL v=((x*M1+y*M2)%M+M)%M; //注意x和y可能是负数!!!
while (Hash[v].vX!=M1) v=(v+1)%M;
Hash[v].vX=x;
Hash[v].vY=y;
Hash[v].id=Id;
}
double Abs(double x)
{
if (x>0.0) return x;
return -x;
}
Hash_data Judge(double x,double y)
{
Hash_data z;
z.vX=M1;
double valX=floor(x+eps),valY=floor(y+eps);
if ( Abs(valX-x)>eps || Abs(valY-y)>eps ) return z;
z.vX=(int)floor(valX+eps);
z.vY=(int)floor(valY+eps);
//注意要写个floor再转int,不要强转,否则-1000000.0001可能转成-999999!!!
return z;
}
bool Check(LL x,LL y)
{
LL v=((x*M1+y*M2)%M+M)%M;
while ( Hash[v].vX!=M1 && ( Hash[v].vX!=x || Hash[v].vY!=y ) ) v=(v+1)%M;
if (Hash[v].vX==M1) return false;
Hash[v].vX=M2;
//注意取出Hash表的元素的时候,最好不要再赋回初始值,否则可能影响下次查找!!!
vis[ Hash[v].id ]=true;
return true;
}
double CJ(data x,data y,data z)
{
data a;
a.X=y.X-x.X;
a.Y=y.Y-x.Y;
data b;
b.X=z.X-x.X;
b.Y=z.Y-x.Y;
return ( a.X*b.Y-a.Y*b.X ); //直接返回叉积
}
int main()
{
freopen("D.in","r",stdin);
freopen("D.out","w",stdout);
for (int i=0; i<M; i++) Hash[i].vX=M1;
scanf("%d",&n);
for (int i=1; i<=n; i++)
{
int x,y;
scanf("%d%d",&x,&y);
Push(x,y,i);
point[i].X=(double)x;
point[i].Y=(double)y;
}
mid.X=mid.Y=0.0;
for (int i=1; i<=n; i++) mid.X+=point[i].X,mid.Y+=point[i].Y;
mid.X/=((double)n),mid.Y/=((double)n);
for (int i=1; i<=n; i++) if (!vis[i])
{
double x=mid.X*2.0-point[i].X;
double y=mid.Y*2.0-point[i].Y;
if ( Abs(x-point[i].X)<=eps && Abs(y-point[i].Y)<=eps )
{
vis[i]=true;
continue;
}
Hash_data z=Judge(x,y);
if ( z.vX!=M1 && Check(z.vX,z.vY) )
Check( (int)floor(point[i].X+eps) , (int)floor(point[i].Y+eps) );
}
int num=0,x=0;
for (int i=1; i<=n; i++) if (!vis[i]) num++,x=i;
if (num<=2)
{
printf("-1\n");
return 0;
}
int ans=0;
for (int y=1; y<=n; y++) if (!vis[y])
{
data z;
z.X=(point[x].X+point[y].X)/2.0;
z.Y=(point[x].Y+point[y].Y)/2.0;
int now=0;
for (int k=1; k<=n; k++) if ( !vis[k] && k!=x && k!=y )
len[++now]=CJ(point[k],mid,z);
sort(len+1,len+now+1);
bool sol=true;
for (int i=1; i<=(now+1)/2; i++)
{
int j=now-i+1;
if (Abs(len[i]+len[j])>eps)
{
sol=false;
break;
}
}
if (sol) ans++,temp[ans]=z;
}
int sum=ans;
for (int i=2; i<=sum; i++)
for (int j=1; j<i; j++)
if ( Abs( CJ(mid,temp[j],temp[i]) )<=eps )
{
ans--; //判断斜率相等的情况!!!
break;
}
printf("%d\n",ans);
return 0;
}