CQOI2017 小Q的草稿

小Q的草稿

小 Q 是个程序员。

众所周知,程序员在写程序的时候经常需要草稿纸。小 Q 现在需要一张草稿纸用来画图,但是桌上只有一张草稿纸,而且是一张被用过很多次的草稿纸。

草稿纸可以看作一个二维平面,小 Q 甚至已经给它建立了直角坐标系。以前每一次草稿使用过的区域,都可以近似的看作一个平面上的一个三角形,这个三角形区域的内部和边界都不能再使用。当然了,以前的草稿也没有出现区域重叠的情况。

小 Q 已经在草稿纸上画上了一些关键点,这些关键点都在没使用过的区域。小 Q 想把这些关键点两两之间尽可能的用线段连接起来。连接两个关键点的线段有可能会穿过已经用过的草稿区域,这样显然不允许。于是小 Q 就想知道,有多少对关键点可以被线段连接起来,而且还不会穿过已经用过的区域。为了方便,小 Q 保证任意三个关键点不会共线。

对于 \(100\%\) 的测试点,\(1\le V,T\le 1000,0 \leq x_i,y_i \leq 10^8\),且都是整数。

题解

首先有显然的\(O(n^2m)\)暴力:枚举两个点判断有没有三角形与线段相交。

然后发现这个判断的过程可以加速。

我们可以枚举其中一个点\(i\),然后对\(j> i\)的点和三角形极角排序+扫描线。

显然对于某个角度范围,我们只需要判断离\(i\)点最近的那条线段是否遮挡了点\(j\)。这个可以用set维护,因为类似圆扫描线,这道题里三角形不相交,所以随着角度的变化,线段的相对顺序不会改变。

注意扫描线的顺序:先加边,在问点,后删边。

小优化:一个三角形只有一条边会有用,就是那条覆盖角度范围最大的那条边。

时间复杂度\(O(n^2\log n)\)

CO double pi=acos(-1);
struct point {double x,y;};

IN point operator+(CO point&a,CO point&b){
	return (point){a.x+b.x,a.y+b.y};
}
IN point operator-(CO point&a,CO point&b){
	return (point){a.x-b.x,a.y-b.y};
}
IN point operator*(CO point&a,double b){
	return (point){a.x*b,a.y*b};
}
IN point operator/(CO point&a,double b){
	return (point){a.x/b,a.y/b};
}
IN double cross(CO point&a,CO point&b){
	return a.x*b.y-a.y*b.x;
}
IN double dot(CO point&a,CO point&b){
	return a.x*b.x+a.y*b.y;
}

IN double angle(CO point&a){
	return atan2(a.y,a.x);
}
IN double len(CO point&a){
	return sqrt(a.x*a.x+a.y*a.y);
}
IN point intersect(CO point&a,CO point&u,CO point&b,CO point&v){
	return b+v*cross(b-a,u)/cross(u,v);
}

CO int N=1e4+10;
point p[N],q[N][3];

struct event{
	double w;
	int o;
	point a,b;
}e[5*N];

IN bool operator<(CO event&a,CO event&b){
	return a.w!=b.w?a.w<b.w:a.o>b.o;
}

struct seg {point a,b;};
double w;

IN bool operator<(CO seg&a,CO seg&b){
	return len(intersect((point){0,0},(point){cos(w),sin(w)},a.a,a.b-a.a))<
		len(intersect((point){0,0},(point){cos(w),sin(w)},b.a,b.b-b.a));
}

set<seg> h;

int main(){
	int n=read<int>(),m=read<int>();
	for(int i=1;i<=n;++i) read(p[i].x),read(p[i].y);
	for(int i=1;i<=m;++i)for(int j=0;j<=2;++j) read(q[i][j].x),read(q[i][j].y);
	int64 ans=0;
	for(int i=1;i<=n;++i){
		int tot=0;
		for(int j=i+1;j<=n;++j)
			e[++tot]=(event){angle(p[j]-p[i]),0,p[j]-p[i]};
		for(int j=1;j<=m;++j){
			pair<double,int> id(0,-1);
			for(int k=0;k<=2;++k){
				double l=angle(q[j][k]-p[i]),r=angle(q[j][(k+1)%3]-p[i]);
				if(l>r) swap(l,r);
				double w=r-l;
				if(w>pi) w=2*pi-w;
				id=max(id,make_pair(w,k));
			}
			int k=id.second;
			double l=angle(q[j][k]-p[i]),r=angle(q[j][(k+1)%3]-p[i]);
			if(l>r) swap(l,r);
			double w=r-l;
			if(w<pi){
				e[++tot]=(event){l,1,q[j][k]-p[i],q[j][(k+1)%3]-p[i]};
				e[++tot]=(event){r,-1,q[j][k]-p[i],q[j][(k+1)%3]-p[i]};
			}
			else{
				e[++tot]=(event){-pi,1,q[j][k]-p[i],q[j][(k+1)%3]-p[i]};
				e[++tot]=(event){l,-1,q[j][k]-p[i],q[j][(k+1)%3]-p[i]};
				e[++tot]=(event){r,1,q[j][k]-p[i],q[j][(k+1)%3]-p[i]};
				e[++tot]=(event){pi,-1,q[j][k]-p[i],q[j][(k+1)%3]-p[i]};
			}
		}
		sort(e+1,e+tot+1);
		for(int j=1;j<=tot;++j){
			w=e[j].w;
			if(e[j].o==1) h.insert((seg){e[j].a,e[j].b});
			else if(e[j].o==-1) h.erase((seg){e[j].a,e[j].b});
			else ans+=h.empty() or len(e[j].a)<
				len(intersect((point){0,0},(point){cos(w),sin(w)},h.begin()->a,h.begin()->b-h.begin()->a));
		}
//		cerr<<i<<" ans="<<ans<<endl;
	}
	printf("%lld\n",ans);
	return 0;
}

不知道为什么LOJ上用C++(NOI)就会挂?可能是编译器版本的问题。

posted on 2020-06-24 20:42  autoint  阅读(128)  评论(0编辑  收藏  举报

导航