[CF886F]Symmetric Projections

Description

给出二维平面上的n个点,求有多少条经过原点的直线满足,所有点在其上的投影是对称的。
若有无数条输出-1
n<=2000

Solution

根据一些脑洞+结论我们知道对称中心一定是原点集的重心在直线上的投影
如果两个点关于重心中心对称那么这两个点在任意一条直线上都是对称的,删去
枚举两个点,钦定它们对称,容易发现直线是唯一的,这样我们得到了O(n^2)条直线
一条直线合法的必要条件是出现过>=n/2次,这样的直线只有O(n)条,可以暴力判断,判断一次是O(n)的
复杂度O(n^2 log n^2),瓶颈在排序

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef double db;

const int N=2e3+5,M=N*N;
const db eps=1e-6;

struct P{
    db x,y;
    P(db _x=0,db _y=0) {x=_x;y=_y;}
    friend P operator + (P a,P b) {return P(a.x+b.x,a.y+b.y);}
    friend P operator - (P a,P b) {return P(a.x-b.x,a.y-b.y);}
    friend P operator * (P a,db x) {return P(a.x*x,a.y*x);}
}p[N],q[N],h;

int n,tot,ans;
bool vis[N];

struct node{
    P p;
    int a,b;
    db k;
}a[N*N];

bool cmp(node a,node b) {return a.k<b.k;}

db cross(P a,P b) {return a.x*b.y-a.y*b.x;}
db dot(P a,P b) {return a.x*b.x+a.y*b.y;}
db len(P a) {return sqrt(dot(a,a));}

int sgn(db x) {
    if (fabs(x)<eps) return 0;
    if (x>0) return 1;
    return -1;
}

bool check(node L,P p) {return fabs(dot(p-h,L.p))<eps;}

int main() {
    scanf("%d",&n);
    fo(i,1,n) {
        scanf("%lf%lf",&q[i].x,&q[i].y);
        h=h+q[i];
    }
    h=h*(1.0/n);
    fo(i,1,n-1)
        fo(j,i+1,n)
            if (!sgn(cross(q[i]-h,q[j]-h))&&fabs(len(q[i]-h)-len(q[j]-h))<eps) {
                vis[i]=vis[j]=1;
                continue;
            }
    int tmp=0;
    fo(i,1,n) if (!vis[i]) p[++tmp]=q[i];
    n=tmp;
    if (n<=2) {puts("-1");return 0;}
    fo(i,1,n-1)
        fo(j,i+1,n) {
            db x=p[i].x+p[j].x-2*h.x;
            db y=p[i].y+p[j].y-2*h.y;
            a[++tot].p.x=y;a[tot].p.y=-x;
            a[tot].a=i;a[tot].b=j;
            if (a[tot].p.x<0) a[tot].p.x=-a[tot].p.x,a[tot].p.y=-a[tot].p.y;
            if (fabs(a[tot].p.x)<eps) a[tot].p.y=fabs(a[tot].p.y);
            if (fabs(a[tot].p.y)<eps) a[tot].p.x=fabs(a[tot].p.x);
            a[tot].k=atan2(a[tot].p.y,a[tot].p.x);
        }
    sort(a+1,a+tot+1,cmp);
    for(int l=1,r=0;l<=tot;l=r+1) {
        while (r<tot&&!sgn(cross(a[r+1].p,a[l].p))) r++;
        if (r-l+1>=n/2) {
            fo(i,1,n) vis[i]=0;
            fo(i,l,r) if (!vis[a[i].a]&&!vis[a[i].b]) vis[a[i].a]=vis[a[i].b]=1;
            int cnt=0,id=0;
            fo(i,1,n) 
                if (!vis[i]) {
                    cnt++;
                    id=i;
                }
            if (cnt>0&&!(n&1)||cnt>1&&(n&1)) continue;
            if (!cnt||check(a[l],p[id])) ans++;
        }
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值