【题解】CF1025F Disjoint Triangles

题意

传送门

给出平面上的一些点,从其中选出6个点,组成两个三角形,求使两个三角形不相交(即平面上不存在一个点同时属于两个三角形)的方案数,如果选的点相同但组成的三角形不同则算不同的方案。

分析

首先有一个结论:如果两个三角形不相交,那么一定存在两条内公切线。如下图(感性理解一下):

VJur90.png

那么我们就可以枚举内公切线了。

对于输入的每一个点,我们让它和其他 n − 1 n-1 n1个点组成直线,并进行极角排序,然后逆时针方向枚举这些直线,同时维护在直线右侧的点的数量,对于每一条直线,利用乘法原理计算方案。

还有一些小细节:每一条直线实际上可以组成两个三角形。如上图中,直线 C E CE CE既可以组成 △ A B C \triangle ABC ABC △ D E F \triangle DEF DEF,也可以组成 △ A B E \triangle ABE ABE △ C D F \triangle CDF CDF。所以每次计算的时候要乘 2 2 2。但是每一个三角形又会被两条直线同时计算,所以最后答案又要减半,那么实际上就完全抵消了。

同时,我们只枚举极角小于 0 0 0的直线,防止共线的情况算重。

代码

#include <bits/stdc++.h>
#define ll long long
#define MAX 2005
using namespace std;

const double PI = acos(-1.0);

struct vec{
    
    
    double x, y;
}a[MAX];

int n;
double b[MAX];
ll ans;

ll C2(ll x){
    
    
    return (x-1)*x/2;
}

int main()
{
    
    
    cin >> n;
    for (int i = 1; i <= n; ++i) {
    
    
        scanf("%lf%lf", &a[i].x, &a[i].y);
    }
    
    for (int i = 1; i <= n; ++i) {
    
    
        int cnt = 0;
        for (int j = 1; j <= n; ++j) {
    
    
            if(i != j){
    
    
                b[++cnt] = atan2(a[j].y-a[i].y, a[j].x-a[i].x);
            }
        }
        sort(b+1, b+cnt+1);
        for (int j = 1, k = 1; j <= cnt && b[j] <= 0; ++j) {
    
    
            while(k <= cnt && b[k]-b[j] < PI){
    
    		//在直线右侧。
                k++;
            }
            ll l = k-j-1, r = cnt-l-1;		//注意一下点的个数细节:当前点和当前k不能算
            ans += C2(l)*C2(r);		//乘法原理
        }
    }
    
    cout << ans << endl;

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_30115697/article/details/90749694
今日推荐