[XSY3381] 踢罐子(几何)

XSY3381

点被选为点对之一的贡献我们单独计算(这部分贡献的总和为 4 n ( n − 1 ) ( n − 2 ) 4n(n-1)(n-2) 4n(n1)(n2))。接下来只讨论剩余部分的贡献。

先把任意三个点构成的六种选择方案合并,发现在外接圆周和弦之间的点每个有2的贡献,在外接圆上的点每个有1的贡献。
在这里插入图片描述
然后考虑任意四个点 A , B , C , D A,B,C,D A,B,C,D的贡献。
发现当四个点构成凸四边形 A B C D ABCD ABCD
∠ A + ∠ D = 180 ° \angle A+\angle D=180\degree A+D=180°
A , B , C , D A,B,C,D A,B,C,D四点共圆,即其中一个点在另外三个点构成的三角形的外接圆上,因此 A , B , C , D A,B,C,D A,B,C,D四个点每个有1的贡献, A , B , C , D A,B,C,D A,B,C,D四点的贡献为4。

∠ A + ∠ D < 180 ° \angle A+\angle D<180\degree A+D<180°
则必有 ∠ B + ∠ C > 180 ° \angle B+\angle C>180\degree B+C>180°
D D D △ A B C \triangle ABC ABC的外接圆外,选 A , B , C A,B,C A,B,C为点对时, D D D无贡献。同理 A A A无贡献。
B B B △ A C D \triangle ACD ACD的外接圆弧和弦之间,选 A , C , D A,C,D A,C,D为点对时, B B B有2的贡献。同理 C C C有2的贡献。 A , B , C , D A,B,C,D A,B,C,D四点的贡献为4。

∠ A + ∠ D > 180 ° \angle A+\angle D>180\degree A+D>180°
则必有 ∠ B + ∠ C < 180 ° \angle B+\angle C<180\degree B+C<180°。类似上种情况。

四个点构成凹四边形时,易证四个点产生的贡献为0。

因此问题转化为统计凸四边形个数。

给出四个构成凸四边形的点,任选两个点连边,有4种情况剩下两个点在连边同侧,2种情况剩下两个点在连边异侧。

给出四个构成凹四边形的点,任选两个点连边,有3种情况剩下两个点在连边同侧,3种情况剩下两个点在连边异侧。
在这里插入图片描述

因此设整张图中有 a a a个凸四边形, b b b个凹四边形,
X = ∑ i < j i , j 连 边 , 再 无 序 地 选 两 个 点 , 选 的 点 在 连 边 同 侧 的 方 案 数 X=\sum_{i<j}i,j连边,再无序地选两个点,选的点在连边同侧的方案数 X=i<ji,j
Y = ∑ i < j i , j 连 边 , 再 无 序 地 选 两 个 点 , 选 的 点 在 连 边 异 侧 的 方 案 数 Y=\sum_{i<j}i,j连边,再无序地选两个点,选的点在连边异侧的方案数 Y=i<ji,j

可列出方程:
{ 4 a + 3 b = X 2 a + 3 b = Y \begin{cases}4a+3b=X\\2a+3b=Y\end{cases} { 4a+3b=X2a+3b=Y

解得:

a = X − Y 2 a=\frac{X-Y}{2} a=2XY

具体实现上,
对于每个点,把剩下的所有点按照和它的连线的斜率排序,求斜率可以用 a t a n 2 l atan2l atan2l函数(加上 l l l避免爆精度)

然后,考虑两个点的连线,设连线两侧的点数分别是 L L L R R R(注意这里要判断,不能构成了一个箭头的形状)

选两个点在连线同侧的有 ( L − 1 ) L + ( R − 1 ) R 2 \frac{(L−1)L+(R−1)R}{2} 2(L1)L+(R1)R种情况,选两个点在连线异侧的有 L × R L\times R L×R种情况。

时间复杂度 O ( n 2 l o g n ) O(n^2logn) O(n2logn)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int N=2010;
int n,tot;
double x[N],y[N],k[N];
ll ans=0;
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*10+ch-'0';ch=getchar();}
    return x*f;
}
int main(){
    
    
    n=read();
    for(int i=1;i<=n;i++){
    
    
        x[i]=read();y[i]=read();
    }
    ans=4ll*n*(n-1)*(n-2);
    for(int i=1;i<=n;i++){
    
    
        tot=0;
        for(int j=1;j<=n;j++){
    
    
            if(i==j) continue;
            k[++tot]=atan2l(x[j]-x[i],y[j]-y[i]);
            if(k[tot]<0) k[tot]+=pi*2;
        }
        sort(k+1,k+tot+1);
        for(int j=1;j<=tot;j++){
    
    
            k[j+tot]=k[j]+pi*2;
        }   
        for(int j=1,t=1;j<=tot;j++){
    
    
            while(t<=tot*2&&(k[t]<k[j]+pi)) t++;
            int l=t-j-1;
            int r=n-1-l-1;
            ans+=(1ll*l*(l-1)/2+1ll*r*(r-1)/2-1ll*l*r)*2ll;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Emma2oo6/article/details/120580357