【XSY3920】简单的几何题(几何,凸包)

题面

简单的几何题

题解

易知 \(v\neq 0\),那么直接考虑条件:

\[\begin{aligned} \left(x_iu^2+x_i-uv-y_iv\right)b^2&\leq \left(x_ia^2+x_i-ab-y_ib\right)v^2\\ \frac{x_iu^2+x_i-uv-y_iv}{v^2}&\leq\frac{x_ia^2+x_i-ab-y_ib}{b^2} \end{aligned} \]

\(u'=\dfrac{u}{v}\)\(v'=\dfrac{1}{v}\)\(a'=\dfrac{a}{b}\)\(b'=\dfrac{1}{b}\),则原不等式变为:

\[x_iu'^2+x_iv'^2-u'-y_iv'\leq x_ia'^2+x_ib'^2-a'-y_ib' \]

考虑配方:

\[\begin{aligned} x_iu'^2+x_iv'^2-u'-y_iv'&\leq x_ia'^2+x_ib'^2-a'-y_ib'\\ u'^2+v'^2-\frac{1}{x_i}u'-\frac{y_i}{x_i}v'&\leq a'^2+b'^2-\frac{1}{x_i}a'-\frac{y_i}{x_i}b'\\ \left(u'^2-\frac{1}{x_i}u'+\frac{1}{4x_i^2}\right)+\left(v'^2-\frac{y_i}{x_i}v'+\frac{y_i^2}{4x_i^2}\right) &\leq \left(a'^2-\frac{1}{x_i}a'+\frac{1}{4x_i^2}\right)+\left(b'^2-\frac{y_i}{x_i}b'+\frac{y_i^2}{4x_i^2}\right)\\ \left(u'-\frac{1}{2x_i}\right)^2+\left(v'-\frac{y_i}{2x_i}\right)^2 &\leq \left(a'-\frac{1}{2x_i}\right)^2+\left(b'-\frac{y_i}{2x_i}\right)^2 \end{aligned} \]

其中左右分别可以看成是 \((u',v')\)\((a',b')\) 到同一个点 \(\left(\dfrac{1}{2x_i},\dfrac{y_i}{2x_i}\right)\) 的距离,其中 \((a',b')\)\(\left(\dfrac{1}{2x_i},\dfrac{y_i}{2x_i}\right)\) 的距离是已知的,记为 \(R_i\)

那么这个不等式就是要求 \((u',v')\) 这个点在以 \(\left(\dfrac{1}{2x_i},\dfrac{y_i}{2x_i}\right)\) 为圆心、\(R_i\) 为半径的圆内或圆上,记这个圆为圆 \(O_i\)\(O_i=\left(\dfrac{1}{2x_i},\dfrac{y_i}{2x_i}\right)\)),那么 \(\odot O_i\) 过点 \((a',b')\)

那么如果 \(i\) 是答案的一种,当且仅当存在点 \((u',v')\) 满足它被 \(\odot O_i\) 包含(“包含” 指含边界),且 \(\forall j\neq i\)\((u',v')\)\(\odot O_j\) 外。也就是存在点 \((u',v')\) 恰好只被圆 \(i\) 包含。

如何找到这些 \(i\) 呢?

我们考虑求出点集 \(\{O,O_1,O_2,\cdots,O_n\}\) 的凸包,那么 \(i\) 是答案的一种当且仅当 \(O_i\) 在凸包上。

感性证明:(以下证明都十分感性,真正的证明应该是用反演变换化曲为直来证)

分情况讨论:

  • \(O\) 不在凸包上。

    那么考虑一个凸包内(不含边界)的点 \(A\),显然一定可以找到两个在凸包上的相邻点 \(B,C\) 使得 \(A\)\(\triangle OBC\) 内,如图:

    在这里插入图片描述

    我们把这个三角形抽出来单独看,作出 \(\odot A,\odot B,\odot C\)

    在这里插入图片描述

    \(\odot A\) 的过点 \(O\) 的直径交 \(\odot A\) 于另一点 \(P_A\),同理设出 \(P_B\)\(P_C\)

    \(\odot B\)\(\odot C\) 交于点 \(Q\),那么容易得到 \(\angle P_BQO=\angle P_CQO=90°\),那么 \(P_B,Q,P_C\) 三点共线。

    容易看出 \(\triangle P_BOP_C\) 是由 \(\triangle BOC\) 位似变换得到,所以 \(A\)\(\triangle BOC\) 内运动可以看作是 \(P_A\)\(\triangle P_BOP_C\) 内运动:(颜色可能有点变化,请不要在意)

    在这里插入图片描述

    那么容易看出 \(\odot B\)\(\odot C\) 的并集一定包含 \(\odot A\)

    所以 \(\odot A\) 所对应的 \(i\) 肯定不符合要求。

  • \(O\) 在凸包上。

    与第一种情况是类似的,对于凸包内(不含边界)的某一个点 \(A\),同样一定可以找到两个在凸包上的相邻点 \(B,C\) 使得 \(A\)\(\triangle OBC\) 内,如图:

    在这里插入图片描述

    接下来的证明过程和第一种情况类似。

感性地证毕。

整道题的大致流程就是这样。

还有一个小细节,你发现如果用 double存点的话精度会爆炸。

然后又发现每一个点都是 \(\left(\dfrac{x}{z},\dfrac{y}{z}\right)\) 的形式,所以我们用三元组 \((x,y,z)\) 存储一个点即可。

然后叉积判断等用 __int128即可,具体详见代码。

代码如下:(细节很多)

#include<bits/stdc++.h>
 
#define N 100010
#define ll long long
#define lll __int128
 
using namespace std;
 
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^'0');
        ch=getchar();
    }
    return x*f;
}
 
struct Point
{
    ll x,y,z;
    int id;
}p[N],st[N];
 
inline int check(Point a,Point b,Point c)
{
    lll x=(lll)(a.x*b.z-b.x*a.z)*(lll)(a.y*c.z-a.z*c.y);
    lll y=(lll)(a.x*c.z-c.x*a.z)*(lll)(a.y*b.z-a.z*b.y);
    if(x==y) return 0;
    return x<y?-1:1;
}
 
inline bool inside(Point a,Point b,Point c)
{
    if(a.x*b.z==b.x*a.z||b.x*c.z==c.x*b.z) return 1;
    return !((a.x*b.z<b.x*a.z)^(b.x*c.z<c.x*b.z));
}
 
inline bool operator < (Point a,Point b)
{
    int tmp=check(p[1],a,b);
    if(!tmp)
    {
        if(p[1].x*a.z<=a.x*p[1].z&&p[1].x*b.z<=b.x*p[1].z) return a.x*b.z<b.x*a.z;
        else return a.x*b.z>b.x*a.z;
    }
    return tmp>0;
}
 
inline bool operator == (Point a,Point b)
{
    return a.x*b.z==b.x*a.z&&a.y*b.z==b.y*a.z;
}
 
int n;
int top;
bool vis[N];
bool ban[N];
 
void Graham()
{
    for(int i=2;i<=n;i++)
        if(p[i].y*p[1].z<p[1].y*p[i].z||
        (p[i].y*p[1].z==p[1].y*p[i].z&&p[i].x*p[1].z<p[1].x*p[i].z))
            swap(p[1],p[i]);
    sort(p+2,p+n+1);
    st[++top]=p[1];
    for(register int i=2;i<=n;i++)
    {
        while(top>1)
        {
            int tmp=check(st[top-1],st[top],p[i]);
            if(!tmp&&st[top]==p[i]) ban[st[top].id]=ban[p[i].id]=1;
            if(tmp<0||(!tmp&&inside(st[top-1],st[top],p[i]))) top--;
            else break;
        }
        st[++top]=p[i];
    }
    for(register int i=1;i<=top;i++) vis[st[i].id]=1;
}
 
int main()
{
    n=read()+1;
    p[1].x=read(),p[1].y=1,p[1].z=read();
    for(register int i=2;i<=n;i++)
        p[i].x=1,p[i].z=2ll*read(),p[i].y=read(),p[i].id=i-1;
    Graham();
    for(register int i=1;i<n;i++)
        if(vis[i]&&!ban[i]) printf("%d ",i);
    return 0;
}
/*
1 2 3
4 5
*/
/*
2 2 3
4 5
6 7
*/
/*
2 1 666666666
333333333 1
455943374 506861437
*/
/*
2 236701642 822463349
213494995 807598793
728662831 347575608
*/
posted @ 2022-10-30 14:02  ez_lcw  阅读(25)  评论(0编辑  收藏  举报