zoukankan      html  css  js  c++  java
  • [Codeforces1036E]Covered Points(计算几何求线段交点)

    [Codeforces1036E]Covered Points(计算几何求线段交点)

    题面

    给出(n)条起点和终点都是整点的线段,问这些线段能够覆盖多少个整点.保证线段两两不共线
    (n leq 1000)

    分析

    先不考虑线段相交的情况,只考虑一条线段(AB)上有多少个整点.设(vec{u}=vec{AB}),那么答案就是(gcd(|vec{u}_x|,|vec{u}_y|))
    线段的交点会被重复算,因此考虑计算线段的交点被多少条线段覆盖,把重复算的减掉.我们两两计算线段交点,用一个map记录整交点出现的次数,如果点(P)出现了(a)次,即有(a)对线段交点为(P),因此线段个数(x)满足(frac{x(x-1)}{2}=a),解得(x=frac{1+sqrt{1+8a}}{2}). 那么就从答案中减去(x-1)即可

    如何判断线段相交和求交点呢,可以用直线两点式爆算。但是有很优美的向量解法,且没有精度误差。

    线段相交的判断

    快速排斥实验:

    以线段(AB)(CD)为对角线做矩形,如果这两个矩形不相交,那么这两条线段也一定不相交
    快速排斥实验.JPG

    (点的标号有点问题,但是应该能看出来)

    把线段投影到(x)轴上,那么显然有(max(A.x,B.x)geq min(C.x,D.x) 且 max(C.x,D.x) geq min(A.x,B.x))
    y轴同理
    这个判断被称为快速排斥实验.但是矩形相交线段就一定相交吗?下面给出了一个反例
    快速排斥实验的反例.JPG

    因此,我们还要用到跨立实验.


    跨立实验:

    如果点(C,D)在直线(AB)的不同侧,则称线段(CD)跨立直线(AB).两线段相交,当且仅当它通过快速排斥实验,且互相跨立。

    跨立实验.JPG

    可以用叉积实现.根据叉积符号的定义知,((vec{AB} imes vec{AD})(vec{AB} imes vec{AC})<0)

    bool is_cross(point A,point B,point C,point D){
    	if(max(A.x,B.x)>=min(C.x,D.x)&&max(C.x,D.x)>=min(A.x,B.x)&&max(A.y,B.y)>=min(C.y,D.y)&&max(C.y,D.y)>=min(A.y,B.y)){//快速排斥实验
            //跨立实验
    		if(sgn(cross(C-A,B-A))*sgn(cross(D-A,B-A))<=0&&sgn(cross(A-C,D-C))*sgn(cross(B-C,D-C))<=0) return 1;//sgn(x)表示x的符号(-1,0,1),不要直接乘,防止溢出 
    		else return 0; 
    	}else return 0;
    }
    

    向量法求线段交点

    如果上一步我们已经知道两线段相交,那么就可以利用叉积求交点。

    交点坐标2.JPG

    根据叉积的几何意义知道,(S_{Delta ADB}=frac{1}{2}|vec{AB} imes vec{AD}|,S_{Delta ACB}=frac{1}{2}|vec{AB} imes vec{AC}|)

    做出三角形的高,显然(frac{CH_2}{H_1H_2}=frac{S_{Delta ACB}}{S_{Delta ADB}})

    根据相似三角形得(vec{CP}=frac{CH_2}{H_1H_2+CH_2}vec{CD}=frac{|vec{AB} imes vec{AC}|}{|vec{AB} imes vec{AD}|+|vec{AB} imes vec{AC}|}vec{CD})

    (vec{CP})知道了,(P)的坐标也就知道了.

    实现上还需要注意一个细节.容易发现除了最后一步的除法之外没有精度误差。那么判断整点只需要在最后一步判余数是否为0即可,不要强行除了之后再取整判断,否则会被卡精度

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring> 
    #include<algorithm>
    #include<cmath>
    #include<map>
    #define maxn 1000
    #define eps 1e-7
    using namespace std;
    typedef long long ll;
    typedef long long ll; 
    ll gcd(ll a,ll b){
    	if(b==0) return a;
    	else return gcd(b,a%b);
    }
    struct Vector{
    	ll x;
    	ll y;
    	Vector(){
    		
    	}
    	Vector(ll _x,ll _y){
    		x=_x;
    		y=_y;
    	}
    	friend Vector operator + (Vector p,Vector q){
    		return Vector(p.x+q.x,p.y+q.y);
    	}
    	friend Vector operator - (Vector p,Vector q){
    		return Vector(p.x-q.x,p.y-q.y);
    	}
    	friend Vector operator * (Vector p,ll k){
    		return Vector(p.x*k,p.y*k);
    	} 
    	friend Vector operator / (Vector p,ll k){
    		return Vector(p.x/k,p.y/k);
    	} 
    };
    typedef Vector point;
    inline ll dot(Vector p,Vector q){
    	return p.x*q.x+p.y*q.y;
    }
    inline ll cross(Vector p,Vector q){
    	return p.x*q.y-p.y*q.x;
    }
    inline ll sgn(ll x){
    	if(x==0) return 0;
    	else if(x>0) return 1;
    	else return -1;
    }
    bool is_cross(point A,point B,point C,point D){
    	if(max(A.x,B.x)>=min(C.x,D.x)&&max(C.x,D.x)>=min(A.x,B.x)&&max(A.y,B.y)>=min(C.y,D.y)&&max(C.y,D.y)>=min(A.y,B.y)){
    		if(sgn(cross(C-A,B-A))*sgn(cross(D-A,B-A))<=0&&sgn(cross(A-C,D-C))*sgn(cross(B-C,D-C))<=0) return 1;//不要直接乘,防止溢出 
    		else return 0; 
    	}else return 0;
    }
    
    int n;
    point A[maxn+5],B[maxn+5];
    map< pair<ll,ll>,ll>vis;
    void get_cross(point A,point B,point C,point D){
    	ll S1=abs(cross(C-A,B-A));
    	ll S2=abs(cross(D-A,B-A));
    	Vector v=D-C;
    	if(v.x*S1%(S1+S2)!=0||v.y*S1%(S1+S2)!=0) return;
    	v=v*S1/(S1+S2);
    	point P=C+v;
    	vis[make_pair(P.x,P.y)]++;
    }
    ll get_ans(ll a){//x*(x-1)/2=a
    	ll x=(1+sqrt(1+8*a))/2;
    	return floor(x);
    }
    int main(){
    	ll ans=0;
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%lld %lld %lld %lld",&A[i].x,&A[i].y,&B[i].x,&B[i].y);
    		Vector v=B[i]-A[i];
    		ans+=gcd(abs(v.x),abs(v.y))+1;
    	}
    //	printf("debug: %lld
    ",ans);
    	for(int i=1;i<=n;i++){
    		for(int j=i+1;j<=n;j++){
    			if(is_cross(A[i],B[i],A[j],B[j])){
    				get_cross(A[i],B[i],A[j],B[j]);
    			}
    		}
    	}
    	for(map< pair<ll,ll>, ll>::iterator it=vis.begin();it!=vis.end();it++){
    		ll tim=it->second;
    		tim=get_ans(tim);
    		ans-=tim-1;
    	}
    	printf("%lld
    ",ans);
    }
    
    
  • 相关阅读:
    自学Linux命令的四种方法
    linux命令学习
    设计师提高效率必备的10大在线工具
    基于CNN网络的汉字图像字体识别及其原理
    字库的产生
    开篇
    智能建筑系统集成发展目标——节能建筑
    建筑节能案例解析:拜尔的生态幼儿园
    能源管理系统(Synchro EMS)技术架构
    能源管理系统(Synchro EMS)
  • 原文地址:https://www.cnblogs.com/birchtree/p/12391777.html
Copyright © 2011-2022 走看看