• [CF gym 100962B]Black Sabbath(voronoi图)


    题面

    http://codeforces.com/gym/100962/attachments B题

    题解

    前置知识

    本题给出n个矩形上两两相离的圆,要求使用m条线段将它们两两隔开。(n{leq}1000,m{leq}10000)

    容易想到voronoi图,但是voronoi图解决的是点的问题,而本题要求的是圆。

    考虑将voronoi图做一个拓展,从寻找两相邻点的中垂线,拓展为寻找两相邻圆的根轴。这样的好处在于它仍然保持了voronoi图原来的性质:因为原来是三个两两相邻的点,它们两两的中垂线共点(即外心);而现在是三个两两相邻的圆,它们两两的根轴共点(即根心)。所以这个拓展后的图仍然能够做到点数、边数均为(O(n))

    时间复杂度:voronoi图最快可以做到(O(n log n)),详见《金牌之路——高中计算机竞赛解题指导》(这就是为什么题目末尾说此题n可以等于1e5)。但是由于太菜写不出来而且此题的n较小,所以就写了(O(n^2 log n))的暴力(就是对于每一个圆做一个半平面交)。

    代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define rg register
    #define eps 1e-9
    #define In inline
    #define N 1000
    
    In int read(){
    	int s = 0,ww = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
    	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
    	return s * ww;
    }
    
    In void write(int x){
    	if(x < 0)putchar('-'),x = -x;
    	if(x > 9)write(x / 10);
    	putchar('0' + x % 10);
    }
    
    In int sgn(double x){
    	if(x < -eps)return -1;
    	return x > eps;
    }
    
    In double sqr(double x){return x * x;}
    
    struct vec{
    	double x,y;
    	vec(){}
    	vec(double _x,double _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);
    	}
    	In friend vec operator * (vec a,double k){
    		return vec(a.x * k,a.y * k);
    	}
    	In friend vec operator / (vec a,double k){
    		return vec(a.x / k,a.y / k);
    	}
    	In friend double Dot(vec a,vec b){
    		return a.x * b.x + a.y * b.y;
    	}
    	In friend double Cross(vec a,vec b){
    		return a.x * b.y - a.y * b.x;
    	}
    };
    
    struct line{
    	vec p,v;
    	line(){}
    	line(vec _p,vec _v){p = _p,v= _v;}
    	In friend bool prl(line a,line b){
    		return sgn(Cross(a.v,b.v)) == 0;
    	}
    	In friend bool samedir(line a,line b){
    		return prl(a,b) && sgn(Dot(a.v,b.v)) == 1;
    	}
    	In friend vec Its(line a,line b){
    		double x = Cross(b.v,a.p - b.p),y = Cross(a.v,b.v);
    		return a.p + a.v * x / y;
    	}
    };
    
    In vec DotOnLine(double A,double B,double C){
    	if(sgn(A) != 0)return vec(-C / A,0);
    	else return vec(0,-C / B);
    }
    
    In bool InUpper(vec a){
    	return sgn(a.y) == 1 || (sgn(a.y)==0&&sgn(a.x)==-1);
    }
    
    In bool cmp(line a,line b){ 
    	bool fa = InUpper(a.v),fb = InUpper(b.v);
    	if(fa ^ fb)return fa < fb;
    	else{
    		int f = sgn(Cross(a.v,b.v));
    		if(f != 0)return f == 1;
    		else return sgn(Cross(b.p-a.p,a.v)) >= 0;
    	}
    }
    
    In bool include(line a,vec p){
    	return sgn(Cross(a.v,p-a.p)) == 1;
    }
    
    In bool check(line a,line b,line c){
    	return include(c,Its(a,b));
    }
    
    int w,h,n;
    
    void HalfPlaneIts(line *l,int n){
    	l[++n] = line(vec(0,0),vec(1,0));
    	l[++n] = line(vec(w,0),vec(0,1));
    	l[++n] = line(vec(w,h),vec(-1,0));
    	l[++n] = line(vec(0,h),vec(0,-1));
    	sort(l + 1,l + n + 1,cmp);
    	deque<line>q;
    	q.clear();
    	for(rg int i = 1;i <= n;i++){
    		if(i > 1 && samedir(l[i],l[i-1]))continue;
    		while(q.size() > 1 && !check(q[q.size()-2],q[q.size()-1],l[i]))q.pop_back();
    		while(q.size() > 1 && !check(q[1],q[0],l[i]))q.pop_front();
    		q.push_back(l[i]);
    	}
    	while(q.size() > 2 && !check(q[q.size()-2],q[q.size()-1],q[0]))q.pop_back();
    	write(q.size()),putchar('
    ');
    	for(rg int i = 0;i < q.size() - 1;i++){
    		vec p = Its(q[i],q[i+1]);
    		printf("%.9lf %.9lf
    ",p.x,p.y);
    	}
    	vec p = Its(q[q.size()-1],q[0]);
    	printf("%.9lf %.9lf
    ",p.x,p.y);
    }
    
    int x[N+5],y[N+5],r[N+5];
    int cnt;
    line l[N+5];
    
    int main(){
    	w = read(),h = read(),n = read();
    	for(rg int i = 1;i <= n;i++)x[i] = read(),y[i] = read(),r[i] = read();
    	for(rg int i = 1;i <= n;i++){
    		cnt = 0;
    		for(rg int j = 1;j <= n;j++){
    			if(i == j)continue;
    			double A = 2 * x[j] - 2 * x[i];
    			double B = 2 * y[j] - 2 * y[i];
    			double C = sqr(x[i]) - sqr(x[j]) + sqr(y[i]) - sqr(y[j]) - sqr(r[i]) + sqr(r[j]);
    			l[++cnt] = line(DotOnLine(A,B,C),vec(y[i]-y[j],x[j]-x[i])); //根轴的方程就是两圆方程相减
    		}
    		HalfPlaneIts(l,cnt);
    	}
    	return 0;
    }
    
  • 相关阅读:
    文化课随笔
    微积分与无穷级数
    [康复计划]-数论基础
    [Codeforces]CF742(Div.2)A-E
    第一次个人编程作业的过程和想法
    第一次个人编程作业
    Python命令行参数及文件读出写入
    第一次个人编程作业
    第一次个人编程作业
    第一次博客作业
  • 原文地址:https://www.cnblogs.com/xh092113/p/12331478.html
Copyright © 2020-2023  润新知