[HDU5784]How Many Triangles

题面

http://acm.hdu.edu.cn/showproblem.php?pid=5784

题解

由于锐角三角形难以计算,我们考虑用(C_n^3)减去钝角、直角三角形和三点共线。这样做的好处是,钝角和直角由于每个三角形中最多一个,只需要枚举一个角即可;而三点共线也只需枚举中间的那个点。

对于每一个点,将其他所有点到该点的连线进行极角排序,然后从排好序的连线中,顺序枚举角的始边,那么直角或钝角的终边就处于始边旋转([{frac{pi}{2},pi}))的范围中,三点共线的终边就是始边的反向。由于“始边旋转([frac{pi}{2},pi))的范围”的左、右端点单调不减,可以时刻维护它们,从而得到对应该始边的直、锐角个数。三点共线个数的统计类似,但是最后应除以2,因为假设A,B,C共线,那么AB为始边会将C计入一次,BC为始边又会将A计入一次。

时间复杂度(O(n^2 log n))。瓶颈为排序。

代码

#include<iostream>
#include<algorithm>

using namespace std;

#define ll long long
#define rg register
#define In inline
#define ld long double

const ll N = 2000;
const ld eps = 1e-18;
const ld pi = acos(-1.0);

In int sgn(ld x){
	return x < -eps ? -1 : x > eps;
}

struct vec{
	ll x,y;
	vec(){}
	vec(ll _x,ll _y){x = _x,y = _y;}
	In friend vec operator + (vec a,vec b){
		return vec(a.x + b.x,a.y + b.y);
	}
	In friend vec operator - (vec a,vec b){
		return vec(a.x - b.x,a.y - b.y);
	}
}p[N+5];

ll n,ln;

ld link[2*N+5];

ll Calc(int k){
	ll L = 1,R = 0,P = 0,s1 = 0,s2 = 0; 
	for(rg int i = 1;i <= k;i++){
		ld n1 = link[i] + pi / 2,n2 = link[i] + pi;
		while(sgn(link[L]-n1) < 0)L++; //L是当前始边的第一个直角或钝角的下标
		while(sgn(link[R+1]-n2) < 0)R++; //R是最后一个直角或钝角的下标
		P = R;
		while(sgn(link[P+1]-n2) <= 0)P++; //P是最后一个平角的下标
		s1 += R - L + 1; 
		s2 += P - R; //平角
	}
	return s1 + (s2 >> 1); //防平角重复
}

int main(){
	while(~scanf("%lld",&n)){
		for(rg int i = 1;i <= n;i++){
			ll x,y;
			scanf("%lld%lld",&x,&y);
			p[i] = vec(x,y);
		}
		ll ans = n * (n - 1) * (n - 2) / 6;
		for(rg int i = 1;i <= n;i++){
			ln = 0;
			for(rg int j = 1;j <= n;j++)
				if(i != j)link[++ln] = atan2l(p[j].y - p[i].y,p[j].x - p[i].x);
			sort(link + 1,link + ln + 1);
			int k = ln;
			for(rg int j = 1;j <= k;j++)
				link[++ln] = link[j] + 2 * pi;
			link[++ln] = 3.5 * pi; //防止溢出
			ans -= Calc(k);
		}
		printf("%lld
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/xh092113/p/12343660.html