Codeforces Round #445 Div1 D:Symmetric Projections (计算几何)

题目传送门: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;
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值