$\dfrac 2\pi$是个引人注目的数字,先来看看它到底是什么东西

假如有一条直线,它和题目所给的某条长度为$d$的线段夹角为$\theta$,那么线段在直线上的投影长度为$\left|d\cos\theta\right|$

现在我们随机一条直线,于是线段的期望投影长度是$\dfrac{\int_0^\pi\left|d\cos\theta\right|d\theta}{\pi-0}=\dfrac2\pi d$

所以我们可以不停随机一个角度,把所有线段投影到这个角度的直线上,一旦检查到总投影长度与原长之比$\geq\dfrac2\pi$就停止

找(?)到合适的角度之后,我们按线段端点投影在直线上的横坐标把所有点分成左右两部分,找一种连线方法使得每条线端的两个端点一个在左边一个在右边,且连线不相交,这样就做完了这道题

当然一些细节是不得不讲的

①随机?问题不大...期望都是$\dfrac2\pi$了,想随机到一个比它大的一点都不难(这也顺带说明了不存在无解的情况)

②设原来线段总长为$len$,原来投影总长为$d$,求得答案的投影总长为$d'$,求得答案的线段总长为$len'$,那么显然有$\dfrac2\pi len\leq d$和$d'\leq len'$

考虑比较$d$和$d'$,我们要求答案的所有线段的端点分列左右两边,对应过来就是投影线段穿越中轴线,如果原来存在两条投影线段不穿越中轴线,我们换一种方式连接即可满足要求,这样一来投影长度还增加了,所以$d\leq d'$

③怎么用合适的方法连线(不相交)

我们可以这样做:每次选取左半边的最左最下点,把其他点做极角排序,扫描的过程中统计扫描线下方左右两边的点数,一旦相同,连一条边并递归上下处理,这样就保证了连线不相交

实现的时候可以不用写成递归的形式,对于分开的上下两组,分别打上不同的标记即可,下次处理到这里只需要找标记相同的点,跟递归差不多的意思gr

然后就做完了,挺愉悦的==

#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
const double pi=3.141592653589793238462643383;
struct point{
	double x,y;
	point(double a=0,double b=0){x=a;y=b;}
}p[10010],a[10010];
double dis(point a,point b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
int match[10010],id[10010],s[10010],c[10010],bl[10010];
bool v[10010];
double ang[10010];
bool cmpx(int x,int y){return a[x].x<a[y].x||(a[x].x==a[y].x&&a[x].y<a[y].y);}
bool cmpa(int x,int y){return ang[x]<ang[y];}
int main(){
	int n,i,j,k,x,y,l,c0,c1,tot;
	double sum,tmp,d,dx,dy;
	scanf("%d",&n);
	for(i=1;i<=n<<1;i++)scanf("%lf%lf",&p[i].x,&p[i].y);
	sum=0;
	for(i=1;i<=n;i++){
		scanf("%d%d",&x,&y);
		sum+=dis(p[x],p[y]);
	}
	while(1){
		d=(rand()%10000)/10000.*2*pi;
		dx=cos(d);
		dy=sin(d);
		for(i=1;i<=n<<1;i++)a[i]=point(p[i].x*dx-p[i].y*dy,p[i].x*dy+p[i].y*dx);
		for(i=1;i<=n<<1;i++)id[i]=i;
		sort(id+1,id+(n<<1|1),cmpx);
		tmp=0;
		for(i=1;i<=n;i++)tmp+=a[id[i+n]].x-a[id[i]].x;
		if(tmp>=2/pi*sum)break;
	}
	for(i=1;i<=n;i++){
		s[id[i]]=0;
		s[id[i+n]]=1;
	}
	tot=0;
	for(i=1;i<=n;i++){
		x=id[i];
		v[x]=1;
		l=0;
		for(j=1;j<=n<<1;j++){
			if(!v[j]&&bl[j]==bl[x]){
				ang[j]=atan2(a[j].y-a[x].y,a[j].x-a[x].x);
				l++;
				c[l]=j;
			}
		}
		sort(c+1,c+l+1,cmpa);
		c0=c1=0;
		for(j=1;j<=l;j++){
			y=c[j];
			if(s[x]!=s[y]&&c0==c1){
				match[x]=y;
				match[y]=x;
				v[y]=1;
				tot++;
				for(k=1;k<j;k++)bl[c[k]]=tot;
				break;
			}
			if(s[y])
				c1++;
			else
				c0++;
		}
	}
	for(i=1;i<=n<<1;i++){
		if(match[i]>i)printf("%d %d\n",i,match[i]);
	}
}

[Contest20180321]nonintersect的更多相关文章

  1. 【XSY2760】nonintersect 计算几何

    题目描述 平面上有\(n\)条线段,你要擦掉所有线段但保留原有的\(2n\)个端点,然后连接这些端点形成\(n\)条不相交的线段,每个端点只能在一条线段中. 假设你画的线段总长为\(Y\),原有线段的 ...

  2. [算法]检测空间三角形相交算法(Devillers & Guigue算法)

    #pragma once //GYDevillersTriangle.h /* 快速检测空间三角形相交算法的代码实现(Devillers & Guigue算法) 博客原地址:http://bl ...

  3. Foundations of Machine Learning: The PAC Learning Framework(1)

    写在最前:本系列主要是在阅读 Mehryar Mohri 等的最新书籍<Foundations of Machine Learning>以及 Schapire 和 Freund 的 < ...

随机推荐

  1. [ST表/贪心] NOI2010 超级钢琴

    [NOI2010]超级钢琴 题目描述 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i ...

  2. B. Minimum Ternary String (这个B有点狠)

    B. Minimum Ternary String time limit per test 1 second memory limit per test 256 megabytes input sta ...

  3. VC++使用CImage在内存中Jpeg转换Bmp图片

    VC++中Jpeg与Bmp图片格式互转应该是会经常遇到,Jpeg相比Bmp在图片大小上有很大优势. 本文重点介绍使用现有的CImage类在内存中进行转换,不需要保存为文件,也不需要引入第三方库. Li ...

  4. VS2010 VC Project的default Include设置

    在IDE中,打开View->Other Windows->Property Manager.展开树形后,你会发现一个名为“Microsoft.Cpp.Win32.user”的项目(如下图) ...

  5. 编写一个 Chrome 浏览器扩展程序

    浏览器扩展允许我们编写程序来实现对浏览器元素(书签.导航等)以及对网页元素的交互, 甚至从 web 服务器获取数据,以 Chrome 浏览器扩展为例,扩展文件包括: 一个manifest文件(主文件, ...

  6. bzoj 3100 排列

    题目大意: 给你长度为 \(1e6\) 的序列, 求最大的 \(K\) 使得序列中含有一个 \(K\) 的排列 做法: 性质: 区间包含1, 元素不重, 区间最大值=区间长度 枚举一个 \(1\) 让 ...

  7. ()()()()x()=()()()()填1-9数字

    /** Function: ()()()()x()=()()()() Developer: Date: */ #include "iostream" #include " ...

  8. 【Foreign】划分序列 [线段树][DP]

    划分序列 Time Limit: 20 Sec  Memory Limit: 256 MB Description Input Output 仅一行一个整数表示答案. Sample Input 9 4 ...

  9. 51nodeE 斜率最大

    题目传送门 这道题只要证明最佳解一定在相邻两个点之间的好啦 这个自己证一证就okay啦 而且我发现n方的算法可以过耶... #include<cstdio> #include<cst ...

  10. ie8以上及非ie8情况下的写法

    <!-- [if gt ie 8]> <!--> <script src="no-ie8.js"></script>  <!- ...