题意
传送门。
给出平面上的一些点,从其中选出6个点,组成两个三角形,求使两个三角形不相交(即平面上不存在一个点同时属于两个三角形)的方案数,如果选的点相同但组成的三角形不同则算不同的方案。
分析
首先有一个结论:如果两个三角形不相交,那么一定存在两条内公切线。如下图(感性理解一下):
那么我们就可以枚举内公切线了。
对于输入的每一个点,我们让它和其他 n − 1 n-1 n−1个点组成直线,并进行极角排序,然后逆时针方向枚举这些直线,同时维护在直线右侧的点的数量,对于每一条直线,利用乘法原理计算方案。
还有一些小细节:每一条直线实际上可以组成两个三角形。如上图中,直线 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;
}