• [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;
    }
    
  • 相关阅读:
    HOJ 2930 Perfect Fill IIl 线性递推
    BZOJ 1269: [AHOI2006]文本编辑器editor Splay
    linux shell常用快捷键(转)
    【引用】Linux date命令
    linux shell if 参数(转)
    vsftpd 530 Permission denied(转)
    捕获非广播包和非发给自己主机的数据包的原理是什么 混杂模式(转)
    代理ARP(转)
    Linux和Unix系统 关系和区别详细介绍(转)
    路由表(转)
  • 原文地址:https://www.cnblogs.com/xh092113/p/12343660.html
Copyright © 2020-2023  润新知