[codeforces 886F]Symmetric Projections

该博客讨论了codeforces 886F问题,涉及二维平面上的点及其投影对称性。文章指出,对称中心是所有点的重心,并通过删除关于重心对称的点简化问题。接着,博主枚举点对,揭示了满足条件的直线数量不超过n条,并给出了O(n^2 log n^2)的整体复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给定二维平面上n个点。问有多少条直线满足:所有点在它上面投影得到的点集是对称的。有无限条输出-1

n≤2000

分析

首先:对称中心一定是原点集的重心在其上面的投影。
接下来:如果两个点关于重心对称,那么它们在任意直线上的投影点都关于重心在其上的投影点对称。可以将这对点删去。

接下来枚举两个点,令它们对称,容易发现对应的直线是唯一的。
那么我们可以得到 n2 条直线,一条直线出现过不小于n次才可能是满足条件的直线。这样的直线不超过n条。判断一次是 O(nlogn) O(n)

整体复杂度是 O(n2logn2)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int N=2005,M=N*N;

typedef long long LL;

typedef double db;

const db z=1e-8;

int n,ans,m,sum;

db dis[N];

bool bz[N];

struct P
{
    db x,y;
    P () {}
    P (db _x,db _y) {x=_x; y=_y;}
}A[N],B[N],H,tmp,Now,zilch;

struct Data
{
    P w; db z;
    int p,q;
}a[M];

bool operator < (Data a,Data b)
{
    return a.z<b.z;
}

db operator * (P a,P b)
{
    return a.x*b.y-a.y*b.x;
}

P Get(P a,P b)
{
    db a0=-a.y,b0=a.x,c0=0,a1=b0,b1=-a0,c1=-a1*b.x-b1*b.y,d=a0*b1-a1*b0;
    return P((b0*c1-b1*c0)/d,(a1*c0-a0*c1)/d);
}

bool Same(db x,db y)
{
    return x-y<z && y-x<z;
}

LL gcd(LL x,LL y)
{
    return (!y)?x:gcd(y,x%y);
}

db getdis(P a,P b)
{
    return ((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

int main()
{
    scanf("%d",&m);
    for (int x,y,i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y); B[i]=P(x,y); H.x+=x; H.y+=y;
    }
    H.x/=m; H.y/=m;
    for (int i=1;i<m;i++) if (!bz[i])
        for (int j=i+1;j<=m;j++) if (!bz[j] && Same(B[i].x-H.x,H.x-B[j].x) && Same(B[i].y-H.y,H.y-B[j].y))
        {
            bz[i]=bz[j]=1; break;
        }
    for (int i=1;i<=m;i++) if (!bz[i]) A[++n]=B[i];
    if (n<2)
    {
        printf("-1\n"); return 0;
    }
    for (int i=1;i<n;i++) for (int j=i+1;j<=n;j++)
    {
        tmp=P((A[i].y+A[j].y)/2-H.y,H.x-(A[i].x+A[j].x)/2);
        if (tmp.x<0)
        {
            tmp.x=-tmp.x; tmp.y=-tmp.y;
        }
        if (tmp.x==0) tmp.y=fabs(tmp.y);
        if (tmp.y==0) tmp.x=fabs(tmp.x);
        a[++sum].w=tmp; a[sum].p=i; a[sum].q=j; a[sum].z=atan2(tmp.y,tmp.x);
    }
    sort(a+1,a+sum+1);
    ans=0;
    for (int i=1,l=1;i<=sum;i++) if (i==sum || a[i].w*a[i+1].w!=0)
    {
        if (i-l+1>=n/2)
        {
            for (int j=1;j<=n;j++) bz[j]=1;
            for (;l<=i;l++)
            {
                if (bz[a[l].p] && bz[a[l].q]) bz[a[l].p]=bz[a[l].q]=0;
            }
            int k=0,cc=0;
            for (int j=1;j<=n;j++) if (bz[j])
            {
                cc++; k=j;
            }
            if (cc>(n&1)) continue;
            if (!k || getdis(Get(a[i].w,H),Get(a[i].w,A[k]))<z) ans++;
        }
        l=i+1;
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值