Pbri

CF886F 做题笔记

题意:

平面上给 \(n\) 个点(坐标都是整数),问有多少条过原点的直线,满足这 \(n\) 个点在这条直线上的投影是一个中心对称图形(对称中心不一定是原点),如果有多个点投影后重合在同一个点,那么只有对应的两个点上重合的点的数量相同时才认为他是中心对称图形。如果有无数条这样的直线,输出 \(-1\)\(n\le 2000\)

题解:

定义中心 \((X,Y)\) 满足 \(X=\dfrac{\sum_{i=1}^nx_i}{n},Y=\dfrac{\sum_{i=1}^ny_i}{n}\) ,那么这些点投影过后的对称中心是中心的投影点。证明:这条直线可以表示为模长为 \(1\) 的向量 \(\begin{bmatrix}a\\b\end{bmatrix}\) ,那么点在这条线上的投影的长度就是与这个向量的点积 \(x\times a+y\times b\) 。那么显然选择这些点的对称中心就是中心,那么复原回去就是中心。于是我们可以提前把每个点的坐标都减去中心的坐标,这样对称中心就一定是原点了。

接下来考虑如何判断一条直线 : \(y=kx\) 是否满足要求。我们考虑一个点在向这条直线的延长线交 \(x\) 轴在 \(w_i\) 处,那么根据全等可以得到:如果两个 \(p,q\) 投影点中心对称,那么 \(w_p=-w_q\) ,然后 \(w\) 推导一下就得到 \(w_i=x_i+k\times y_i\) ,然后我们把所有的 \(w\) 计算出来之后排序,然后头尾一个一个匹配即可。

接下来考虑哪些直线有可能成为答案,我们枚举每一对点 \((p,q)(q>p)\) ,那么根据 \(w_p=-w_q\) 可以得到 \(x_p+k\times y_p=-(x_q+k\times y_q)\) ,于是我们可以解出 \(k\)

可这样还是太多,总共会有 \(O(n^2)\) 条直线去判断,每次判断是 \(O(n\log n)\) 的。考虑到一条线如果符合条件那么一定会有 \(\dfrac{n}{2}\) 对数的答案是他,那么我们找出所有出现次数大于等于 \(\dfrac{n}{2}\)\(k\) 去判断。那么总共会有不超过 \(O(n)\) 条直线。

\(code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <cmath>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 2010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
	int w=0,flg=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
	while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
	return w*flg;
}
int n,ans;
struct point{
	db a,b;
}p[MAXN];
bool cmp(point x,point y)
{
	return x.b<y.b;
}
db X,Y,re[MAXN];
vector<db>vc,w;
vector<int>ct;
bool vis[MAXN];
int ck(db k)
{
	//printf("ck :k=%.1lf\n",k);
	FUP(i,1,n) re[i]=p[i].a+p[i].b*k;
	//FUP(i,1,n) printf("%.1lf ",re[i]);
	//puts("");
	sort(re+1,re+n+1);
	//FUP(i,0,(int)(w.size())-1) printf("%.1lf ",w[i]);
	//puts("");
	int l=1,r=n;
	for(;l<r;l++,r--)
	{
		//printf("l=%d r=%d ct[l]=%d ct[r]=%d\n",l,r,ct[l],ct[r]);
		if(fabs(re[r]+re[l])>eps) return 0;
	}
	//printf("l==r!!!\n");
	if(l==r)
	{
		if(fabs(re[l])<=eps) return 1;
		else return 0;
	}
	return 1;
}
int main(){
	n=read();
	FUP(i,1,n)
	{
		p[i].a=read(),p[i].b=read();
		X+=p[i].a,Y+=p[i].b;
	}
	X/=n,Y/=n;
	//printf("X=%lf Y=%lf\n",X,Y);
	FUP(i,1,n) p[i].a-=X,p[i].b-=Y;
	sort(p+1,p+n+1,cmp);
	//FUP(i,1,n) printf("(%.0lf,%.0lf)\n",p[i].a,p[i].b);
	FUP(i,1,n)
	{
		if(vis[i]) continue;
		FUP(j,i+1,n)
		{
			if(vis[j]) continue;
			if(fabs(p[i].a+p[j].a)<=eps&&fabs(p[i].b+p[j].b)<=eps)
			{
				vis[i]=vis[j]=true;
				break;
			}
		}
	}
	int tmp=0;
	FUP(i,1,n) if(!vis[i]) tmp++;
	if(tmp<=2){puts("-1");return 0;}
	//printf("X=%lf Y=%lf n=%d\n",X,Y,n);
	FUP(i,1,n)
	{
		if(vis[i]) continue;
		FUP(j,i+1,n)
		{
			if(vis[j]) continue;
			if(fabs(p[i].b+p[j].b)<=eps)
			{
				if(fabs(p[i].a+p[j].a)<=eps)
				{
					vc.push_back(0);
					continue;
				}
				ans=1;
				int l=1,r=n;
				while(l<r)
				{
					if(fabs(p[l].b+p[r].b)>eps)
					{
						ans=0;
						break;
					}
					while(l+1<r&&p[l+1].b==p[l].b) l++;
					while(l+1<r&&p[r-1].b==p[r].b) r--;
					if(r==l+1) break;
					l++,r--;
				}
				continue;
			}
			//if((2*X-p[i].a-p[j].a)/(p[i].b+p[j].b-2*Y)==0.5) printf("i=%d j=%d k=%lf\n",i,j,(2*X-p[i].a-p[j].a)/(p[i].b+p[j].b-2*Y));
			vc.push_back((-p[i].a-p[j].a)/(p[i].b+p[j].b));
		}
	}
	sort(vc.begin(),vc.end());
	//FUP(i,0,(int)(vc.size())-1) printf("%lf ",vc[i]);
	//puts("");
	FUP(i,0,(int)(vc.size())-1)
	{
		int l=i,r=i;
		while(r<(int)(vc.size()))
		{
			if(vc[r]-vc[l]<=eps) r++;
			else break;
		}
		r--;
		//printf("l=%d r=%d\n",l,r);
		if(r-l+1<tmp/2) continue;
		ans+=ck(vc[l]);
		i=r;
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-09-03 18:54  Pbri  阅读(70)  评论(0编辑  收藏  举报