7011. 2021.03.13【2021省赛模拟】nonintersect

平面上有若干条线段,设其总长为\(X\)。现在需要把这些线段拆开,点两两匹配成为新的线段,要求这些线段不相交。设其总长为\(Y\),需要满足\(\frac{2}{\pi}X\le Y\)

\(n\le 5000\)


考虑\(\frac{2}{\pi}\)是什么。随机一条直线的倾斜角,一条线段在这个直线上投影的长度和自身长度的比为\(|\cos\theta|\),积分一下得到期望\(\frac{2}{\pi}\)

根据期望的线性性,随机这条直线之后,所有线段的投影的期望长度和为\(\frac{2}{\pi}X\)

于是必然可以找到一个角度\(\theta\),使得所有线段的投影的期望长度大于等于这个值。(由期望的定义得。否则期望会更小)

假设找到了这个角度,把点投影到这条线段上,把前后\(n\)个点分为黑点和白点,互相匹配。匹配之后,投影和的长度肯定大于等于\(\frac{2}{\pi}X\)

不考虑相交时,显然存在完美匹配。现在把边权设为欧几里得距离,此时最小权完美匹配一定无交。于是证明了无交的完美匹配一定存在。

选择一个\(x\)坐标最小的点(如果相同\(y\)坐标最小)拉出来,对其它点极角排序,找到一个和这个点颜色相异的,并且两点连线之后把点集分开成两个黑白点个数相同的点集的点,分治下去做。这样最坏情况下\(O(n^2\lg n)\)

问题是怎么找到\(\theta\):可以发现投影和的长度是关于倾斜角的分段函数,有\(O(n)\)段,每段是\(a\cos x+b \sin x\)的形式。在每段找出极值即可,最终取最大的即可。这一部分时间\(O(n\lg n)\)(段间的分界点需要排序)


using namespace std;
#include <bits/stdc++.h>
#define N 10005
#define fi first
#define se second
#define mp(x,y) make_pair(x,y)
const double PI=acos(-1);
int n;
struct DOT{double x,y;} d[N];
DOT operator+(DOT a,DOT b){return {a.x+b.x,a.y+b.y};}
DOT operator-(DOT a,DOT b){return {a.x-b.x,a.y-b.y};}
double dot(DOT a,DOT b){return a.x*b.x+a.y*b.y;}
double cro(DOT a,DOT b){return a.x*b.y-a.y*b.x;}
double len(DOT a){return sqrt(dot(a,a));}
pair<int,int> ed[N];
double le[N],theta[N];
int m;
pair<double,int> o[N];
int sign[N];
double a,b;
double ans,anss;
double calc(double x){return a*cos(x)+b*sin(x);}
void upd(double l,double r){
	if (calc(l)>anss) anss=calc(l),ans=l;
	if (calc(r)>anss) anss=calc(r),ans=r;
	double t=(a!=0?atan(b/a):PI/2);
	if (l<t && t<r && calc(t)>anss) anss=calc(t),ans=t;
}
int c[N];
pair<double,int> p[N];
bool cmpang(pair<double,int> a,pair<double,int> b){
	return a.fi<b.fi || a.fi==b.fi && d[a.se].x<d[b.se].x;
}
pair<int,int> ls[N];
int nl;
void divide(int q[],int n){
	if (n==0)
		return;
	int v=q[1];
	for (int i=2;i<=n;++i)
		if (d[q[i]].x<d[v].x || d[q[i]].x==d[v].x && d[q[i]].y<d[v].y)
			v=q[i];
	int k=0;
	for (int i=1;i<=n;++i)
		if (q[i]!=v){
			DOT u=d[q[i]]-d[v];
			p[++k]=mp(atan2(u.y,u.x),q[i]);
		}
	sort(p+1,p+k+1,cmpang);
	int s=c[v];
	for (int i=1;i<=k;++i){
		s+=c[p[i].se];
		if (s==0){
			ls[++nl]=mp(v,p[i].se);
			for (int j=1;j<=k;++j)
				q[j]=p[j].se;
			divide(q,i-1);
			divide(q+i,n-i-1);
			return;
		}
	}
}
void doit(){
	DOT v={cos(ans),sin(ans)};
	for (int i=1;i<=n*2;++i)
		p[i]=mp(dot(d[i],v),i);
	sort(p+1,p+n*2+1);
	for (int i=1;i<=n;++i) c[p[i].se]=1;
	for (int i=n+1;i<=n*2;++i) c[p[i].se]=-1;
	static int q[N];
	for (int i=1;i<=n*2;++i)
		q[i]=i;
	divide(q,n*2);
}
int main(){
//	freopen("nonintersect.in","r",stdin);
//	freopen("nonintersect.out","w",stdout);
	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n*2;++i)
		scanf("%lf%lf",&d[i].x,&d[i].y);
	for (int i=1;i<=n;++i){
		int p,q;
		scanf("%d%d",&p,&q);
		DOT v=d[p]-d[q];
		le[i]=len(v);
		theta[i]=atan2(v.y,v.x);
		if (theta[i]<0)
			theta[i]+=PI;
		if (theta[i]<PI/2){
			o[++m]=mp(theta[i]+PI/2,i);
			sign[i]=1;
		}
		else{
			o[++m]=mp(theta[i]-PI/2,i);
			sign[i]=-1;
		}
		a+=le[i]*cos(theta[i])*sign[i];
		b+=le[i]*sin(theta[i])*sign[i];		
	}
	sort(o+1,o+m+1);
	o[0].fi=0;
	for (int i=1;i<=n;++i){
		upd(o[i-1].fi,o[i].fi);
		sign[o[i].se]*=-1;
		a+=le[o[i].se]*cos(theta[o[i].se])*sign[o[i].se]*2;
		b+=le[o[i].se]*sin(theta[o[i].se])*sign[o[i].se]*2;
	}
	upd(o[n].fi,PI);
	doit();
	for (int i=1;i<=nl;++i)
		printf("%d %d\n",ls[i].fi,ls[i].se);
	return 0;
}
posted @ 2021-03-14 19:28  jz_597  阅读(98)  评论(0编辑  收藏  举报