codeforces 886F Symmetric Projections

190 篇文章 2 订阅
48 篇文章 0 订阅

题面

题意

在一个二维平面上有n个点,问有几条过原点的直线,使n个在直线上的投影中心对称。

做法

首先发现这条直线是否合法仅与它的斜率有关,然后发现这条直线必过这n个点的重心,因此,关于重心中心对称的点对可以直接去掉(它们在任何过重心的直线下中心对称),然后我们可以暴力枚举每个点与那个点对称,确定一对即可得到一条直线,可以暴力枚举 n 2 n^2 n2组点对,这样一条合法直线至少被得到 n 2 \frac{n}{2} 2n次,最多得到 O ( n ) O(n) O(n)条可能合法的直线,对每条直线暴力判断一下即可。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstring>
#define db double
#define eps 1e-8
#define N 2010
using namespace std;

int n,ans;
db sx,sy,d[N];
bool gg[N];
struct Node
{
	db x,y,jj;
	Node operator + (const Node &u) const{return (Node){x+u.x,y+u.y};}
	Node operator - (const Node &u) const{return (Node){x-u.x,y-u.y};}
	db operator * (const Node &u) const{return x*u.x+y*u.y;}
	Node operator / (const db &u) const{return (Node){x/u,y/u};}
	bool operator == (const Node &u) const{return fabs(x-u.x)<eps && fabs(y-u.y)<eps;}
	void calc(){if(fabs(y)>eps) jj=atan(x/y);}
	bool operator < (const Node &u) const
	{
		if(fabs(y)<eps)
		{
			if(fabs(u.y)<eps) return x<u.x;
			return 1;
		}
		if(fabs(u.y)<eps) return 0;
		return jj<u.jj;
	}
}node[N];
struct Xn
{
	Node u,v;
	void rot()
	{
		swap(v.x,v.y);
		v.x=-v.x;
	}
};
vector<Node>use;

inline db cj(Node u,Node v){return u.x*v.y-u.y*v.x;}
inline db lenf(Node u,Node v){return (u.x-v.x)*(u.x-v.x)+(u.y-v.y)*(u.y-v.y);}
inline db len(Node u,Node v){return sqrt(lenf(u,v));}
inline db dx(Node u,Xn v){return fabs(cj(u-v.u,v.v))/len(v.u,v.u+v.v);}

inline bool check(Xn u)
{
	u.rot();
	int i,j;
	db t;
	for(i=1;i<=n;i++)
	{
		d[i]=(node[i]-u.u)*u.v/sqrt(u.v*u.v);
	}
	sort(d+1,d+n+1);
	t=d[1]+d[n];
	for(i=2;i<n;i++)
	{
		if(fabs(d[i]+d[n-i+1]-t)>eps) return 0;
	}
	return 1;
}

int main()
{
	int i,j;
	use.clear();
	ans=sx=sy=0;
	memset(gg,0,sizeof(gg));
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%lf%lf",&node[i].x,&node[i].y);
		sx+=node[i].x;
			sy+=node[i].y;
	}
	sx/=(db)n;
	sy/=(db)n;
	for(i=1;i<=n;i++)
	{
		node[i].x-=sx;
		node[i].y-=sy;
	}
	for(i=1;i<=n;i++)
	{
		if(gg[i]) continue;
		for(j=i+1;j<=n;j++)
		{
			if(gg[j]) continue;
			if(fabs(node[i].x+node[j].x)<eps && fabs(node[i].y+node[j].y)<eps) break;
		}
		if(j<=n) gg[i]=gg[j]=1;
	}
	for(i=1,j=0;i<=n;i++)
	{
		if(gg[i]) continue;
		node[++j]=node[i];
	}
	n=j;
	if(n<=2)
	{
		puts("-1");
		return 0;
	}
	for(i=1;i<=n;i++)
	{
		for(j=i+1;j<=n;j++)
		{
			use.push_back((node[i]+node[j])/2.0);
			use[use.size()-1].calc();
		}
	}
	sort(use.begin(),use.end());
	for(i=0;i<use.size();i=j)
	{
		for(j=i+1;j<use.size();j++)
		{
			if(fabs(cj(use[i],use[j]))>eps) break;
		}
		if(j-i>=n/2)
		{
			ans+=check((Xn){(Node){0,0},use[i]});
		}
	}
	printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值