codeforces 886F 889D 890F Symmetric Projections

 

这题我写吐了,交了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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值