How Many Triangles
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 767 Accepted Submission(s): 249
Problem Description
Alice has n points in two-dimensional plane. She wants to know how many different acute triangles they can form. Two triangles are considered different if they differ in at least one point.
Input
The input contains multiple test cases.
For each test case, begin with an integer n,
next n lines each contains two integers xi and yi .
3≤n≤2000
0≤xi,yi≤1e9
Any two points will not coincide.
For each test case, begin with an integer n,
next n lines each contains two integers xi and yi .
3≤n≤2000
0≤xi,yi≤1e9
Any two points will not coincide.
Output
For each test case output a line contains an integer.
Sample Input
3 1 1 2 2 2 3 3 1 1 2 3 3 2 4 1 1 3 1 4 1 2 3
Sample Output
0 1 2
数一数锐角的数量A和直角+钝角的数量B,那么答案就是(A-2B)/3。暴力算的话是$O(n^3)$的。使用极角排序+two pointers就可以做到$O(n^2log\ n)$。 这边钝角指代范围在90度到180度之间的角(不包括90和180)。
直接统计锐角三角形较困难,考虑问题的反面,统计直角三角形、钝角三角形、平角三角形(暂时这么叫吧QAQ)。
首先枚举三角形的一个端点A,对其他点进行象限为第一关键字,极角为第二关键字排序。
然后使用三个指针,进行O(n)的扫描。
具体做法为用 i 指针指向三角形的第二个端点B。我们可以假想通过平移和旋转,把A点置于平面直角坐标系的原点,把B点置于x轴的正方向。那么可以与AB组成钝角或直角的点就在三四象限或者y轴。
将 j 指针指向第一象限内可以组成锐角的最靠后的点,将k指针从j + 1 开始扫描至最后一个可以组成钝角的点,然后统计对答案的贡献。
之后将 i 指针 +1,继续扫描。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const double eps = 1e-8;
struct point {
long long x, y;
int f;//代表在哪一个象限
friend point operator - (point a, point b) {
return (point){a.x-b.x, a.y-b.y};
}
}a[2005], b[2005];
point s;
int n, m;
long long cross(point a, point b, point c) {//ab X ac
return (b.x-a.x) * (c.y-a.y) - (b.y-a.y) * (c.x-a.x);
}
int cal(point a) {//计算象限
if(a.x > 0 && a.y >= 0) return 1;
if(a.x <= 0 && a.y > 0) return 2;
if(a.x < 0 && a.y <= 0) return 3;
if(a.x >= 0 && a.y < 0) return 4;
}
bool cmp(const point a, const point b) {//先按象限排序,再按极角排序
if(a.f < b.f) return true;
if(a.f > b.f) return false;
long long tmp = cross(s, a, b);
if(tmp > 0) return true;
return false;
}
bool ok(point a, point b, point c) {
long long tmp = (b.x-a.x) * (c.x-a.x) + (b.y-a.y) * (c.y - a.y);
if(tmp > 0) return true;
return false;
}
int main() {
while(~scanf("%d", &n)) {
for(int i = 0; i < n; i++) {
scanf("%I64d%I64d", &a[i].x, &a[i].y);
}
long long sum = 0;//统计除了锐角三角形的其他三角形的个数
for(int p = 0; p < n; p++) {//枚举三角形的一个顶点
s = a[p];
m = 0;
for(int i = 0; i < n; i++) {
if(i != p) b[m++] = a[i];
}
for(int i = 0; i < m; i++) {
b[i].f = cal(b[i]-s);
}
sort(b, b+m, cmp);//极角排序
//two pointers
int i = 0, j = 0, k = 0;
while(j < m && ok(s, b[i], b[j]) && cross(s, b[i], b[j]) >= 0) {
j++;
}
if(j == m) continue;
k = j; j--;
while(i < m) {
if(!ok(s, b[i], b[k])) {
while(k < m-1 && !ok(s, b[i], b[k+1])) k++;
sum += k - j;
}
i++;
if(j < i) j = i;
while(j < m-1 && ok(s, b[i], b[j+1]) && cross(s, b[i], b[j+1]) > 0) {
j++;
}
if(k <= j) k = j + 1;
if(k >= m) break;
}
}
long long ans = (long long)1 * n * (n-1) * (n-2) / 6;
ans -= sum;
printf("%I64d\n", ans);
}
return 0;
}