这题我写吐了,交了31发,最后一发AC,尝试了各种写法以及各种假优化,最后从别人的代码片段中获得灵感,感慨还是自己太蠢了。。。
题意:
n个点,有一条过原点的直线,如果所有点在这条直线上的投影点是关于某个点对称的,那么这条直线就是GOOD,问这样直线的个数。
题解:
首先要求出所有点的重心。
如果一条直线是GOOD,那么n个点的投影点一定是关于重心的投影点对称的。
第一步,将关于重心对称的点对去掉,因为这样的点在任意直线上的投影点一定是关于重心的投影点对称的,就不用管他们了。
看剩下的点
如果没有剩下的点,说明GOOD直线有无数条。
在所有的GOOD直线中,第1个点一定有一个对称点,所以我们枚举和第一个点对称的点,这样那条直线就确定下来了,我们再看其他点的投影点是否对称,是的话,答案+1.
具体做法:
找到重心
去除无用的点
将重心当做原点,更新剩余点的坐标
枚举和第一个点对称的点,确定直线,方法如下:
若两个点为(a,b)、(c,d) 那么这两个点的投影点关于原点对称情况下的直线的斜率为 -(a+c)/(b+d)
利用向量的点积表示投影的长度,将所有点的投影排序后,看是否对称就可以了
代码:
#include<bits/stdc++.h>
#include<hash_map>
#define N 2010
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 3.141592653589793
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lower_bound
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
LL x[N],y[N];
bool c[N];
typedef pair<LL,LL> pp;
LL q[N*N];
map<pp,int>mp;
int main()
{
int n,m;LL dx=0,dy=0;
sc(n);
for (int i=0;i<n;i++)
{
int xx,yy;
scc(xx,yy);
x[i]=xx;y[i]=yy;
dx+=x[i];
dy+=y[i];
x[i]*=n;
y[i]*=n;
}
for (int i=0;i<n;i++)
for (int j=i;j<n;j++)
if (dx*2==x[i]+x[j] && dy*2==y[i]+y[j]) c[i]=c[j]=1;
m=0;
for (int i=0;i<n;i++) if (!c[i])
{
x[m]=x[i];
y[m]=y[i];
m++;
}
if (m==0)
{
puts("-1");
return 0;
}
for (int i=0;i<m;i++)
{
x[i]-=dx;
y[i]-=dy;
}
int ans=0;
for (int i=0;i<m;i++)
{
LL xx=x[i]+x[0],yy=y[i]+y[0];
LL t=__gcd(xx,yy);
xx/=t;yy/=t;
dx=-yy,dy=xx;
if (mp[pp(dx,dy)]) continue;
for (int j=0;j<m;j++) q[j]=dx*x[j]+dy*y[j];
sort(q,q+m);
int l=0,r=m-1;
while(l<=r)
{
if (q[l]!=q[r]*-1) break;
l++;r--;
}
if (l>r)
{ans++;mp[pp(dx,dy)]=1;}
}
cout<<ans;
}